0x00题目
http://123.206.120.239/
0x01步骤
这里看到xdebug,以为是phpstorm开了远程调试,因为题目说管理员开发完登陆界面就睡着了,2333
尝试打了payload,无果,参考文章 http://momomoxiaoxi.com/2017/09/18/WHCTF/
那你就是admin,不然你就是guest
这题的正确做法是输入很长的code,让pre_match处理的时候出错,php进程崩掉,然后你后面的guest身份的插入语句就不会执行
赛后知道这个解法后,我真的是惊呆了。。。比赛时实在是搜不到能绕过pre_match的方法,因为这个给了头尾^$,以后搜不到还是得多看看php文档。。。
我比赛用的方法很匪夷所思,赛后跟出题人交流才知道原因,这是他的答复
导致数据库的user表和identities表清空的频率很快
同时也有条件竞争的解法
其实这里的条件竞争,还有另外一处
login.php里面会清理session
memeber.php的会检测session
那么如果我们用脚本不断得注册并访问login.php,当member刚运行完此处
$_SESSION['is_logined'] 和 $_SESSION['is_guest'] 刚好在login.php那边被清除掉,接着member.php继续往下走,执行
这样if里面的判断就是 0 || 0 ,自然能进入下面的else逻辑
绕过了guest,来到文件读取处
进入下面else逻辑,开始读取文件,但is_file()判断是文件,就不让读取,不过好在readfile()函数支持php伪协议,成功绕过is_file() 读取config.php
payload:
http://123.206.120.239/member.php?file=php://filter/read=convert.base64-encode/resource=config.php
0x02后端源码: login.php
<?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");
?>
register.php
<?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>';
member.php
<?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>';
走过路过,欢迎纠错~