Training: MySQL I 总结
题目
题目总结分为四个部分
- 解题思路及部分答案
- 个人疑惑以及疑惑解答
- 常见的SQL注入语句
- 收获:通过本地模拟环境测试
1.解题思路及部分答案
这是一个简单的SQL注入题目,对于初初初学者学者的我来说连源代码都看不懂,所以就一点一点详细道来吧!
首先源代码解析:
/* 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;
这部分就是创建一个表users,表的三个列分别为userid,username,password
# Username and Password sent? if ( ('' !== ($username = Common::getPostString('username'))) && (false !== ($password = Common::getPostString('password', false))) ) { auth1_onLogin($chall, $username, $password); }
这部分是判断用户名和密码是否已经发送给服务器,其中的Common::getPostString是调用已有函数,功能为从表单中获取字符串信息;auth1_onLogin为下边的自定义函数,功能为验证用户名和密码
/** * Get the database for this challenge. * @return GDO_Database */ function auth1_db() { if (false === ($db = gdo_db_instance('localhost', WCC_AUTH_BYPASS1_USER, WCC_AUTH_BYPASS1_PASS, WCC_AUTH_BYPASS1_DB))) { die('Database error 0815_1!'); } $db->setLogging(false); $db->setEMailOnError(false); return $db; }
这部分代码其实我还不是很清楚,我认为是初始化一个数据库实例,用来后续操作,这部分不影响完成题目
/** * Exploit this! * @param WC_Challenge $chall * @param unknown_type $username * @param unknown_type $password * @return boolean */ function auth1_onLogin(WC_Challenge $chall, $username, $password) { $db = auth1_db(); $password = md5($password); $query = "SELECT * FROM users WHERE username='$username' AND password='$password'"; if (false === ($result = $db->queryFirst($query))) { echo GWF_HTML::error('Auth1', $chall->lang('err_unknown'), false); # Unknown user return false; } # Welcome back! echo GWF_HTML::message('Auth1', $chall->lang('msg_welcome_back', htmlspecialchars($result['username'])), false); # Challenge solved? if (strtolower($result['username']) === 'admin') { $chall->onChallengeSolved(GWF_Session::getUserID()); } return true; }
这部分就是之前提到的自定义函数,用来处理表单提交的数据。函数与表单之间通过参数“username”和“password”传递数据,这里只需要知道从html表单中输入的username和password就可以代入到这里的query语句中就可以了。
接着 result= db->queryFirst($query)处理MySQL语句并且将结果返回给result,如果查询结果不是false,说明结果存在。
$result[‘username’]) === ‘admin’判断用户是否是admin,如果是说明管理员登录。
完成登陆过程。
然后说明注入过程:这里要想办法输入username和password使得执行query语句的结果存在并且用户名为Admin。但是由于我们不知道真实的admin的密码,所以我们想办法绕过password。即我们可以减少select的限制条件,通过将query中AND及其后边的语句在MySQL语句中注释掉,就可以将query改写为:
SELECT * FROM users WHERE username='admin'
于是需要知道MySQL中的注释语法:MySQL注释
这里我们选择单行注释,即’#’和’–’
答案 :那么构造username分别为:username=admin’#和username=admin’– (注意这里–后边要加括号,原因见MySQL注释);password不填
2.个人疑惑及其解答
解题思路为“注释”,于是我认为下边的填写才正确;
username=admin' ";#
如果这样填写,源代码中的query就变成了
$query = "SELECT * FROM users WHERE username='admin'";#' AND password='$password'";
如此一来既保证了语法正确,而且还将#后边的内容注释掉了(因为#也是php语句的注释)。但结果并不是这样的,我们将不同的username输入之后得到的结果如下:
我本以为第三种情况才是正确的,因为这样得到的&query才是完整的,既有封闭的“”,还有分号作为php结束的标志,完全符合语句的标准。而像第一种情况得到的&query后边缺少分号,难道不会报语句完整性错误吗??
通过请教他人和查阅资料,发现php语句可以没有最后的分号作为结尾(但是前提是后边的分块的结尾符号,这里后边的注释语句。。。不知是否可行),这样的话,第一种情况就可以说的过去了;
“php变量的内容不能阶段php语句”
这句话点醒了为什么第三种情况不能成功注入了,这里的MySQL语句在注释之前多了一个”使得语法错误,不能通过。
这里很重要的一点是,输入的username中的注释都只能注释掉MySQL中的后边句子,而不能截断原来的php语句!!!
下边是MySQL SQL 和 PHP的注释语法
3.常见的SQL注入语句测试
’ or ‘1’=’1
’ or 1=1 –
‘or’=’0’
“or 1=1 –
or 1=1 –
‘or’ a ‘=’ a
“or” a “=” a
师傅所传授!暂时还没有实践使用,先记下来
4.在解答疑惑的过程中的收获
师傅是这样解答我的疑惑的,他通过搭建一个本地的php的web环境,通过样例,给定输入得到输出,从而证明并解释了变量中的注释符号不能截断php的现象。所以,这样的解决问题的思路还是很值得学习的,加油!