Training: MySQL II (MySQL, Exploit, Training)
题目描述
This one is the same as MySQL1, but you have to come up with a more advanced injection to trick this authentication.
Your mission is again: Login yourself as admin.
Again you are given the sourcecode, also as highlighted version.Enjoy!
这与MySQL1相同,但您必须使用更高级的注入来欺骗这种身份验证。
你的任务还是:以管理员身份登录。
同样,您会得到源代码,这是是高亮显示的版本。
享受吧!
解
查看源代码
<?php
/* TABLE STRUCTURE
CREATE TABLE IF NOT EXISTS users (
userid INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
password CHAR(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL
) ENGINE=myISAM;
*/
# Username and Password sent?
if ( ('' !== ($username = Common::getPostString('username'))) && (false !== ($password = Common::getPostString('password', false))) ) {
auth2_onLogin($chall, $username, $password);
}
/**
* Get the database for this challenge.
* @return GDO_Database
*/
function auth2_db()
{
if (false === ($db = gdo_db_instance('localhost', WCC_AUTH_BYPASS2_USER, WCC_AUTH_BYPASS2_PASS, WCC_AUTH_BYPASS2_DB))) {
die('Database error 0815_2!');
}
$db->setLogging(false);
$db->setEMailOnError(false);
return $db;
}
/**
* Exploit this! It is the same as MySQL-I, but with an additional check, marked with ###
* @param WC_Challenge $chall
* @param unknown_type $username
* @param unknown_type $password
* @return boolean
*/
function auth2_onLogin(WC_Challenge $chall, $username, $password)
{
$db = auth2_db();
$password = md5($password);
$query = "SELECT * FROM users WHERE username='$username'";
if (false === ($result = $db->queryFirst($query))) {
echo GWF_HTML::error('Auth2', $chall->lang('err_unknown'), false);
return false;
}
#############################
### This is the new check ###
if ($result['password'] !== $password) {
echo GWF_HTML::error('Auth2', $chall->lang('err_password'), false);
return false;
} # End of the new code ###
#############################
echo GWF_HTML::message('Auth2', $chall->lang('msg_welcome_back', array(htmlspecialchars($result['username']))), false);
if (strtolower($result['username']) === 'admin') {
$chall->onChallengeSolved(GWF_Session::getUserID());
}
return true;
}
?>
<form action="index.php" method="post">
<table>
<tr>
<td><?php echo $chall->lang('username'); ?>:</td>
<td><input type="text" name="username" value="" /></td>
</tr>
<tr>
<td><?php echo $chall->lang('password'); ?>:</td>
<td><input type="password" name="password" value="" /></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name="login" value="<?php echo $chall->lang('btn_login'); ?>" /></td>
</tr>
</table>
</form>
MySQL II和MySQL I大同小异,区别在于MySQL II将password部分的内容单独进行校验。
function auth2_onLogin(WC_Challenge $chall, $username, $password)
{
$db = auth2_db();
$password = md5($password);
$query = "SELECT * FROM users WHERE username='$username'";
if (false === ($result = $db->queryFirst($query))) {
echo GWF_HTML::error('Auth2', $chall->lang('err_unknown'), false);
return false;
}
#############################
### This is the new check ###
if ($result['password'] !== $password) {
echo GWF_HTML::error('Auth2', $chall->lang('err_password'), false);
return false;
} # End of the new code ###
#############################
echo GWF_HTML::message('Auth2', $chall->lang('msg_welcome_back', array(htmlspecialchars($result['username']))), false);
if (strtolower($result['username']) === 'admin') {
$chall->onChallengeSolved(GWF_Session::getUserID());
}
return true;}
根据auth2_onLogin函数分析可知此次我们需要注入的sql语句是query = “SELECT * FROM users WHERE username=’$username’”,更上一次不同,这次密码的检测不能进行绕过,通过 $result = $db->queryFirst($query)) 和 $result[‘password’] !== $password 能够知道result中存储了sql查询的返回值(userid,username,password),故通过重新构造sql语句使返回的password是输入的password的md5的值即可。
要想控制sql的返回值,首先要了解sql的select查询语句,通常使用select查询语句的格式为:select [列名] from [表名] where [条件表达式] 。这里我在TEST数据库中创建了demo表,demo表中就一列数据id,使用select对表中数据进行查询结果如图(*是选取所有列的快捷方式)。
但如果我们不按照select的查询格式直接查询id = ‘10’ 会得到相同的结果,只不过此处的id不是demo表中的id,相当于select直接返回了id = ‘10’ 并没有对数据位置进行检查。知道了这些就可以使用select语句构造我们想要的数据,得到一个与题目的数据库user表相同格式的数据。
构造出假的管理员的用户名和密码,如何才能骗过查询让它认为我们构造的数据在数据库中,这里就要用到sql中的union操作符
通过下图可以看到id = ‘1’ 是我们通过select直接返回的数据,并且使用unino将其和demo表中的数据合并,但id = ‘1’这个并不在demo中通过这种方法就可以使题目以为我们构造的的管理员账号和密码在user表中。
这里我使用123’ union select 1,‘admin’,'21232f297a57a5a743894a0e4a801fc3 使query中的sql语句变为 SELECT * FROM users WHERE username=‘123’ union select 1,‘admin’,‘21232f297a57a5a743894a0e4a801fc3’ 因为123不在users表中,故返回值只有
注:此处1,‘admin’,'21232f297a57a5a743894a0e4a801fc3’对应users表中userid、username、password顺序,其中21232f297a57a5a743894a0e4a801fc3是admin的md5值,即对应密码为admin。
用户名 123’ union select 1,‘admin’,'21232f297a57a5a743894a0e4a801fc3
密码 admin