DDCTF web题复现
DDCTF自己只做出来第一道web,后面都是一点思路没有,所以在比赛后看wp进行复现了。
web签到题
进入页面后显示这个
查看源代码发现这个js文件有点可疑
虽然第一眼看到phpstorm就想到.idea/workspace.xml,但是这里好像没有,但是看到这个uri,以及设置的header,所以就访问这个url,并且构造didictf_username为admin。
发现返回了一段json数据,并且其中的数据有一部分是unicode编码,所以解码
根据返回内容,就去访问图中的这个url
返回的内容中包含了两个文件的源代码
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Mon, 22 Apr 2019 05:54:30 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 4936
<html>
<head lang="en">
<meta charset="UTF-8">
<title>HighLightjs</title>
<link rel="stylesheet" href="../highlight/styles/default.css">
<script src="../highlight/highlight.pack.js"></script>
<body>
<script>
hljs.initHighlightingOnLoad();
</script>
<pre>
<code class="php">
url:app/Application.php
</code>
<code class="php">
Class Application {
var $path = '';
public function response($data, $errMsg = 'success') {
$ret = ['errMsg' => $errMsg,
'data' => $data];
$ret = json_encode($ret);
header('Content-type: application/json');
echo $ret;
}
public function auth() {
$DIDICTF_ADMIN = 'admin';
if(!empty($_SERVER['HTTP_DIDICTF_USERNAME']) && $_SERVER['HTTP_DIDICTF_USERNAME'] == $DIDICTF_ADMIN) {
$this->response('您当前当前权限为管理员----请访问:app/fL2XID2i0Cdh.php');
return TRUE;
}else{
$this->response('抱歉,您没有登陆权限,请获取权限后访问-----','error');
exit();
}
}
private function sanitizepath($path) {
$path = trim($path);
$path=str_replace('../','',$path);
$path=str_replace('..\\','',$path);
return $path;
}
public function __destruct() {
if(empty($this->path)) {
exit();
}else{
$path = $this->sanitizepath($this->path);
if(strlen($path) !== 18) {
exit();
}
$this->response($data=file_get_contents($path),'Congratulations');
}
exit();
}
}
</code>
</pre>
<br />
<pre>
<code class="php">
url:app/Session.php
</code>
<code>
include 'Application.php';
class Session extends Application {
//key建议为8位字符串
var $eancrykey = '';
var $cookie_expiration = 7200;
var $cookie_name = 'ddctf_id';
var $cookie_path = '';
var $cookie_domain = '';
var $cookie_secure = FALSE;
var $activity = "DiDiCTF";
public function index()
{
if(parent::auth()) {
$this->get_key();
if($this->session_read()) {
$data = 'DiDI Welcome you %s';
$data = sprintf($data,$_SERVER['HTTP_USER_AGENT']);
parent::response($data,'sucess');
}else{
$this->session_create();
$data = 'DiDI Welcome you';
parent::response($data,'sucess');
}
}
}
private function get_key() {
//eancrykey and flag under the folder
$this->eancrykey = file_get_contents('../config/key.txt');
}
public function session_read() {
if(empty($_COOKIE)) {
return FALSE;
}
$session = $_COOKIE[$this->cookie_name];
if(!isset($session)) {
parent::response("session not found",'error');
return FALSE;
}
$hash = substr($session,strlen($session)-32);
$session = substr($session,0,strlen($session)-32);
if($hash !== md5($this->eancrykey.$session)) {
parent::response("the cookie data not match",'error');
return FALSE;
}
$session = unserialize($session);
if(!is_array($session) OR !isset($session['session_id']) OR !isset($session['ip_address']) OR !isset($session['user_agent'])){
return FALSE;
}
if(!empty($_POST["nickname"])) {
$arr = array($_POST["nickname"],$this->eancrykey);
$data = "Welcome my friend %s";
foreach ($arr as $k => $v) {
$data = sprintf($data,$v);
}
parent::response($data,"Welcome");
}
if($session['ip_address'] != $_SERVER['REMOTE_ADDR']) {
parent::response('the ip addree not match'.'error');
return FALSE;
}
if($session['user_agent'] != $_SERVER['HTTP_USER_AGENT']) {
parent::response('the user agent not match','error');
return FALSE;
}
return TRUE;
}
private function session_create() {
$sessionid = '';
while(strlen($sessionid) < 32) {
$sessionid .= mt_rand(0,mt_getrandmax());
}
$userdata = array(
'session_id' => md5(uniqid($sessionid,TRUE)),
'ip_address' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
'user_data' => '',
);
$cookiedata = serialize($userdata);
$cookiedata = $cookiedata.md5($this->eancrykey.$cookiedata);
$expire = $this->cookie_expiration + time();
setcookie(
$this->cookie_name,
$cookiedata,
$expire,
$this->cookie_path,
$this->cookie_domain,
$this->cookie_secure
);
}
}
$ddctf = new Session();
$ddctf->index();
</code>
</pre>
</body>
</html>
首先去找可以读到flag的函数,再一步步找引用它的地方,再通过满足题目所设置的条件,来实现flag文件的读取。这道题目看到了魔术方法 _destruct(),最近碰到的反序列化有点多,这里很简单,只要我们传入一个序列化的对象Application并且它的属性path为我们要读取的flag文件,就可以读到flag了。所以现在就去找代码中实现了反序列化的地方。可以看到在函数session_read()函数中的第三个if语句之后有反序列化的实现,所以我们就要满足前面所设置的条件。
1、存在cookie
2、存在session
3、md5判断相等
前两格条件好满足,但是第三个要求我们传入的session,这里的session被赋值为cookie,而hash为cookie的最后32位。也就是说我们传入的cookie最后32位要等于MD5(eancrykey),所以我们现在要知道eancrykey的值是什么。
在我圈出的地方有一个可以读取出eancrykey的方法,我们可以传递一个参数nickname,而后面会用sprintf进行输出,那如果我们将nickname设置成%s,那么在第二次的时候就会将eancrykey输出。第一次为welcome my friend %s,第二次为,welcome my friend %s, eancrykey。这样就可以得到key的值了。所以我们先啥都不设置,去访问session.php,就可以由服务器去设置session和cookie。
然后我们带上它的cookie再访问一次
nickname设置为#%s#,key就会在##中间输出。那现在就可以构造我们的payload
首先构造序列化对象 O:11:"Application":1:{s:4:"path";s:21:"..././config/flag.txt";}
这里的..././是因为../会被替换为空字符,而flag的目录在get_key函数中提示了。这样又刚好为18个字符。再对这个进行urlencod,因为要满足格式嘛。再把md5加密后的(key+serilize(session))放在后面。
前一天做出来的时候就去睡觉了,所以忘了给flag截图,找了一个师傅wp上的最后的效果图。。。
2、homebrew event loop
这道题目是一个python写的flask框架,传入的参数会被trigger_event函数存入session的log中,在execute_event_loop里参数会被循环处理,action:后面作为函数名 ;后的作为参数值,在try的内容中会执行传入的函数。所以可以通过将第一个函数设置为trigger_event添加函数,后面可以继续添加函数和参数。
但是要得到flag我们还要去找能读取flag的函数,136行定义的show_flag看似是一个可用的函数,但flag读取的是我们设置的参数的值,就算设置为了FLAG()也无法读取到,而且注释里提示了已经将这个方法禁止了。再看到get_flag_handler,如果 num_items的值大于等于5,就会调用show_flag,并且由服务端设置了参数为FLAG(),所以我们只能通过这个方法才能读到flag。所以现在要求num_items的值大于等于5,发现了 buy函数和consume_point函数,先由buy函数将num_items的值设置为传入的参数值args,再由consume_point函数去判断,points是否大于num_items,如果不大于就会报错。
所以这里出现了一个逻辑漏洞,我们执行buy函数去得到的num_items会先传入trigger_event中,也就是会被写入session的log中,而flask的session其实是保存在本地的,也叫作客户端session,是可以被读取的。
所以我们现在的思路就是,先执行buy函数并且设置参数为5,然后再去执行get_flag函数,虽然consume_point函数会报错,但是我们的flag已经被写到了本地的session中,只要读取就行了。
所以构造的payload为/?action:trigger_event%23;action:buy;5%23action:get_flag;
#要设置为%23,因为是get传参,直接填#会被认为是锚链接而读取不到后面的参数,为什么buy_handler只填一个buy就可以了,是因为eval中自动添加了。
然后去解密session,用p神的脚本。最前面的一个点别忘了,不然爆不出来。
p神讲解的文章链接在这:https://www.leavesongs.com/PENETRATION/client-session-security.html
后面的待更
上一篇: JAVA一道易错的基础题!!!
推荐阅读
-
web面试题必问的题
-
赛题类型 Web、Crypto、Pwn、Reverse、Misc 各是指什么意思?
-
Codeforces Round #277.5 (Div. 2)(C题)_html/css_WEB-ITnose
-
PHP开发工程师面试真题之Web网页设计(附参考答案)
-
20190423 CTF题-web
-
Codeforces Round #257 (Div. 2) A. Jzzhu and Children(简单题)_html/css_WEB-ITnose
-
Codeforces Round #248 (Div. 2) A题_html/css_WEB-ITnose
-
Codeforces Round #271 (Div. 2) F题 Ant colony(线段树)_html/css_WEB-ITnose
-
Codeforces Round #271 (Div. 2) F题 Ant colony(线段树)_html/css_WEB-ITnose
-
Codeforces Round #281 (Div. 2) (A、B、C、D题)_html/css_WEB-ITnose