滴~
base64->base64->hex
?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09 = ?jpg=666C61672E6A7067 =flag.php
尝试包含index.php1
/index.php?jpg=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3
base64解码得到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
/*
* 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?
*
*/
f1agconfigddctf.php = TmpZek1UWXhOamMyTXpabU5tVTJOalk1TmpjMk5EWTBOak0zTkRZMk1tVTNNRFk0TnpBPQ==
解码得到:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
include('config.php');
$k = 'hello';
extract($_GET);
if(isset($uid))
{
$content=trim(file_get_contents($k));
if($uid==$content)
{
echo $flag;
}
else
{
echo'hello';
}
}
WEB 签到题
考点:1
sprintf()格式化字符串漏洞
发现调用了Auth.php,并且有个didictf_username参数
访问Auth.php
当加上didictf_username = admin这个头
访问 app/fL2XID2i0Cdh.php
其中app/Application.php1
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
44Class Application {
var $path = ''; //定义一个公共变量$path
public function response($data, $errMsg = 'success') {
$ret = ['errMsg' => $errMsg, //创建一个php数组,其中从形参获取两个值转化为数组值。
'data' => $data];
$ret = json_encode($ret);
header('Content-type: application/json'); //定义http头json内容类型
echo $ret; //显示数据加密后的json数据
}
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
$path = trim($path); //删除$path首位空
$path=str_replace('../','',$path); // ../ 替换为 空
$path=str_replace('..\\','',$path); // ..\\ 替换为 空
return $path; //返回$path
}
public function __destruct() {
if(empty($this->path)) { // 如果$path为空,就直接结束了
exit();
}else{
$path = $this->sanitizepath($this->path); // 不为空就调用sanitizepath()做替换
if(strlen($path) !== 18) { //如果处理过的$path长度不严格等于18,就跳出
exit();
}
$this->response($data=file_get_contents($path),'Congratulations');
} // 把$path读取出来以后传入response()
exit();
}
}
app/Session.php1
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
112include '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()) { //HTTP_DIDICTF_USERNAME: admin时才能继续执行
$this->get_key(); //调用get_key()
if($this->session_read()) { //如果session_read()返回为TRUE就进入下面三行
$data = 'DiDI Welcome you %s';
$data = sprintf($data,$_SERVER['HTTP_USER_AGENT']);
parent::response($data,'sucess');
}else{//如果session_read()返回为FLASE,就调用session_create()
$this->session_create();
$data = 'DiDI Welcome you';
parent::response($data,'sucess');
}
}
}
private function get_key() { // 获取eancrykey,hint: ../config/flag.txt
//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();
Application.php里面是一个Application类,有一个为空的公共变量$path,三个公共方法response()、auth()和__destruct(),还有一个私有方法sanitizepath()。
response()方法能把传入的两个数据用json格式化后打印出来,auth()方法进行访问者的权限校验,刚开始的didictf_username就是在此进行校验,还有一个__destruct(),这是析构方法,是在方法中最后一定会执行的函数,这个方法里面的意思就是:如果$path不为空,就把$path用sanitizepath()过滤一下,去除一次../和..\\
,然后过滤后的路径的长度如果等于18,就进行读取,再把内容丢给response()方法进行显示。
接下来分析session.php
生成一个 32 位的随机session_id字符串,然后$userdata就是最后 Session 中的数据。里面包含了我们的 IP,浏览器 UA 等信息。最后会对这个存有信息的$userdata数据进行 PHP 序列化。同时会把序列化的结果,与密钥$eancrykey拼接,做一次 md5 后拼接在结果后面,起到签名防伪造的效果。
最后将整个结果以 Cookie 的形式存在用户本地。加了个$eancrykey做 md5,这确实是个不错的本地存 Cookie 的方法啊
读取的过程其实也就是相反的。首先是检测有没有名为ddctf_id的 Cookie,没有的话就返回FALSE。如果有的话,截取后面 32 位,即那段 md5 的签名。再截取前面的内容,即数据正文。用加密的方式检验一下 Cookie 有没有被篡改。没有的话,就对数据进行 PHP 反序列化。检查数据中的session_id ip_address ip_address元素是否存在。
然后判断一下是否传入了名为nickname的 POST 参数,如果有的话,就格式化字符串输出出来。后面还对ip_address和ip_address做了检验。
这里我们传入修改后的 Cookie,都需要用$eancrykey进行检验,因此得想办法得到$eancrykey的值,这样就可以伪造 Cookie 了。
其中在get_key() 注释写到 flag和eanccrykey在同一个文件夹
因此得想办法得到$eancrykey的值,这样就可以伪造 Cookie 了。
在传入nickname POST 参数时,十分违和地出现了个$eancrykey。这里把$_POST["nickname"]
和$this->eancrykey放到了同一个数组中,使用foreach循环后再使用了格式化字符串sprintf()。这里想到了一个很机智的方法:我们只需要 POST 请求app/Session.php,传入nickname的值为%s,第一次格式化字符串时,会把”Welcome my friend %s”中的%s换成%s,第二就会把%s换成$this->eancrykey,这样我们就得到$eancrykey了。
注意,格式化字符串的代码是在session_read()方法里的,因此我们需要先空白请求一次,拿到一个 Cookie,再带着这个 Cookie 请求,这样才能进入session_read()方法。
解下来获取$eancrykey
注意: 将get改为post,同时加上Content-Type: application/x-www-form-urlencoded
拿到$eancrykey为EzblrbNS
接下来进行反序列化
这里给了个 hint//eancrykey and flag under the folder,因此我们需要想办法读取../config/文件夹下的 flag。在Application类中的析构函数中,有一个file_get_contents()可供我们读取文件。其中的参数$path来自于Application类中的$path属性。而$path属性的初始值是空的。根据这里含有魔术方法__destruct(),并且后面有unserialize()函数,想到我们尝试可以通过 PHP 反序列化来修改$path的值。
构造pop链
1 |
|
ddctf_id = a:4:{s:10:”session_id”;O:11:”Application”:1:{s:4:”path”;s:21:”…/./config/flag.txt”;}s:10:”ip_address”;s:15:”xxx.xxx.xxx.xxx”;s:10:”user_agent”;s:2:”ua”;s:9:”user_data”;s:0:””;}4d7f704737a8721321d75d580eea9cb2
编码提交,得到flag
upload img
考点 : GD库二次渲染
上传一张图片,然后下载下来,发现和原来的图片发生了变化,上传正常图片都被处理过了
需要注意的是无论上传什么图片都会被转化为 jpg,所以使用 jpg 来构造更好。
直接脚本
https://github.com/BlackFan/jpg_payload
Usage: php jpg_payload.php <jpg_name.jpg>
用这个工具生成可以GD渲染处理后,依然能保留字符串的jpg,在py源码中把字符串改为phpinfo(),然后生成。
homebrew event loop
1 | # flag获取函数 |
大吉大利,今晚吃鸡
尝试sql注入 无果
注册账号 登入 点击购买,支付 发现余额不足,查看页面 发现请求了这个页面
开始伪造价格,发现后端按范围是分别以32位和64位处理,因为64位最大整数+1报错,32位最大整数+1报错,然而其中间的某范围数不报错。经过测试发现提交32位最大整数*2+2到100的数就可买票,比如4294967296
进到游戏之后,可以通过提交正确id与ticket来移除对手,没什么好办法,经过一番尝试了解到id与ticket是固定对应的关系,只有写脚本通过暴力注册获取尽可能多的id,然后一一删除
然后批量注册小号批量买入场券批量拿id和token给大号淘汰
1 |
|