前言
用了三四天终于把南邮ctf平台的web题做了差不多,也算对ctf多了一些了解,之前发过一篇综合题(2)的writeup,这次准备从头整理一遍。这两天通过做题学到了很多,整理的会很详细!
writeup
1.签到题
直接看源代码就可以看到flag了
2.md5 collision
php源码
1 | $md51 = md5('QNKCDZO'); |
php弱类型的利用,这道题的思路蛮常见的,需要传入一个参数a不等于 ‘QNKCDZO’,但是经过md5加密后的值需要与 “QNKCDZO”的md5加密后的值进行松散比较相等。
md5(‘QNKCDZO’)=’0e830400451993494058024219903391’,而PHP在处理哈希字符串时,会利用!=
或==
来对哈希值进行比较,它把每一个以0E
开头的哈希值都解释为0
,所以如果两个不同的密码经过哈希以后,其哈希值都是以0E
开头的,那么PHP将会认为他们相同,都是0
。
所以给 a 传入一个 md5 加密后也为 0e 开头的字符串即可,这样的字符串一搜一大把了
这里我传了 ?a=s878926199a
拿到flag
tips:
1、在PHP中,@被称为错误控制操作符,前置@符号的表达式产生的任何错误都将被忽略。
2、1992年发布的MD5算法是一种广泛使用的哈希算法,最初被设计用来作为加密算法,在被证明不安全后只能用来做数据完整性校验。MD5算法为消息产生128位摘要,常表示为32位或16位十六进制串,由[0-9a-e]组成。
3、PHP的比较操作符主要有两类——松散比较和严格比较,于是就有了equal(==)和Identical(===)两种相等,主要区别在于前者会在比较前根据上下文对操作数进行类型转换而后者不会。
3.签到2
这道题是前端对输入框长度的限制,F12查看页面代码发现 maxlength="10"
而需要输入的口令 “zhimakaimen为11位,直接在查看器更改10为任意大于等于11的数再输入口令即可。
4.这题不是web
打开题目地址发现一个可爱的动图,把图片下载到本地,用文本编辑器或winHex打开(记事本就可以了),Ctrl + F 查找flag,在末尾。
5.层层递进
又是一道查看源代码的题,在\<iframe>标签中跟着链接依次访问嵌套的SO.html
-> S0.html
->SO.htm
->S0.htm
->404.html
,在404.html注释中找到flag
6.AAencode
javascript aaencode
这道题链接坏了,在南邮旧ctf平台找到了这道题的链接
打开题目是乱码,用火狐Unicode编码后是颜文字
提示是js aaencode,是一种把js代码编码成颜文字的编码方式,直接把这段颜文字拉到控制台执行,发现有错误
错误提示:ω゚ノ is not defined
,即 ω゚ノ
这个变量没有被定义,那我们先在控制台定义这个变量,再执行代码
拿到flag
7.单身二十年
访问 http://chinalover.sinaapp.com/web8/search_key.php 会被重定向到 http://chinalover.sinaapp.com/web8/no_key_is_here_forever.php ,重定向会被浏览器自动处理,burp抓包repeater重放一下拿到flag。
8.php decode
1 | <?php |
应该是一道编码题,但是可以直接在代码在线运行的网站允许一下这段php代码,就可以跑出flag了
9.文件包含
使用php的filter协议读取index.php
payload:http://4.chinalover.sinaapp.com/web7/index.php?file=php://filter/read=convert.base64-encode/resource=./index.php
显示了base64编码后的index.php源码,这种方法经常用得到,把得到的编码在在线网站解码得到源码
1 | <html> |
查看源码得到flag
更多php伪协议实现命令执行的姿势可见 传送门
10.单身一百年也没用
这道题的flag藏在了响应信息中
11.Download~!
这道题链接找不到了,提示是别下音乐
看网上的解题是进去之后会看到两个下载音乐的链接,查看源代码发现星星点灯的下载链接为download.php?url=eGluZ3hpbmdkaWFuZGVuZy5tcDM=
,经过了base64加密,解码内容为xingxingdiandeng.mp3。
尝试下载其他页面如index.php download.php及它们base64加密后的页面,发现download.php可以下载
打开下载的download文件,发现首先对url参数进行了base64解码,并且只有四个文件能够正常下载,否则提示Access Forbidden!下载hereiskey.php,得到flag 。
12.COOKIE
TIP: 0==not
F12看网络,把请求头中的Cookie:Login=0
改成 Login=1
重新发包后在响应主体中拿到flag
13.MYSQL
根据提示查看robots.txt (robots.txt是一个告诉爬虫那些东西不能爬的文件,在ctf中是一个经常存在的有提示信息的文件)
1 | TIP:sql.php |
说明要向sql.php提交一个id,使得intval($_GET[id])
为1024而$_GET[id]==1024
为假。
这里有两种做法
1.传入id=1024.1,intval()函数会把1024.1取整为1024
2.利用intval()函数的特性:只识别到非数字的那一位,而松散比较的强制类型转换会把e当作科学记数法的一部分处理,即科传入id=1024e1
14.GBK Injection
把id参数的值改成2,3得到两个提示信息:gbk_sql_injection 和 the fourth table
在参数后面加个单引号,发现sql语句转译成了your sql:select id,title from news where id = '3\''
判断存在GBK宽字节注入, flag在第四个表中,用sqlmap一下就跑出来了哈哈
payload:
python2 sqlmap.py -u http://chinalover.sinaapp.com/SQL-GBK/index.php?id=1 -T ctf4 -C flag --dump
手工注入一下:
先介绍宽字节注入的原理如下,
当传入 id=1’ 时,单引号会被转义符(反斜线)转义,导致id参数无法逃逸单引号的包围。由于MySQL执行查询时会跳过畸形字符,当输入参数 id=1%df’ 时,参数会经过转义和url编码变为 id=1%df%5c%27,%5c 是反斜杠的url编码,%27 是单引号的url编码。而在GBK编码中,%df%5c是繁体字”連 “,这时单引号成功逃逸。
1 | #第一步 判断是否存在注入 |
15./x00
这道题学到了点东西!
1 | view-source: |
要求提交的nctf的值符合正则匹配(一个或多个数字)并且能被strpos()找到#biubiubiu
有两种方法:
1.ereg()正则函数会把null视为字符串的结束,从而被%00截断,而strpos则可以越过%00
所以提交nctf=1%00%23biubiubiu
,即可拿到flag
2.ereg和strpos函数传入数组后返回的都是NULL
所以提交nctf[]=
,拿到flag
tips:
由于在PHP中string的实现本质上是一个以字节为单位的数组加上一个声明缓冲区长度的整形,因此string类型可以由任何值构成,即使是“NUL bytes”,但PHP中有些底层库(比如C语言相关的,因为C语言中\0标识字符串的结束)会忽略”a NUL byte”后面的数据,使用了这些库的函数就是非二进制安全的(non-binary-safe),ereg就是一个例子,还有很多可以自行百度。
16.bypass again
依旧是弱类型
1 | if (isset($_GET['a']) and isset($_GET['b'])) { |
和第二题原理一样
payload:http://chinalover.sinaapp.com/web17/index.php?a=QNKCDZO&b=s878926199a
17.变量覆盖
source.php查看源代码,关键php代码整理后如下
1 | <?php |
extract() 函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
第二个参数 type 用于指定当某个变量已经存在,而数组中又有同名元素时,extract() 函数如何对待这样的冲突。
该函数返回成功导入到符号表中的变量数目。
所以这道题给pass和thepassword_123用post传一个相同的值即可
18.php是世界上最好的语言
这道题链接也挂了,我没答上
参考一下网上的writeup吧
index.txt核心代码如下
1 | <?php |
eregi()函数判断id是否为hackerDJ,大小写敏感。网页会拒绝任何含有hackerDJ
的提交(忽略大小写),但接受urldecode后为hackerDJ
的字符串。
因为url在传入后台时会自动先进行一次url解码,所以这里需要二次编码
hackerDJ
url编码后为%68%61%63%6b%65%72%44%4a
二次编码后为%2568%2561%2563%256b%2565%2572%2544%254a
其中%25是%的url编码
19.伪装者
提示管理系统只能在本地登陆
改了Referer: 127.0.0.1
和X-Forwarded-For: 127.0.0.1
都没有成功
看网上的解释好像是服务端代码有问题
XFF头用以标志客户端真实IP,常用在使用HTTP 代理或者负载均衡服务
20.header
链接挂了,flag应该就在数据包的头信息中
21.上传绕过
介绍一下文件截断绕过攻击
1.文件系统0x00截断
在上传的时候,当文件系统读到【0x00】时,会认为文件已经结束。利用00截断就是利用程序员在写程序时对文件的上传路径过滤不严格,产生0x00上传截断漏洞。通过抓包截断将【evil.php.jpg】后面的一个【.】换成【0x00】。在上传的时候,当文件系统读到【0x00】时,会认为文件已经结束,从而将【evil.php.jpg】的内容写入到【evil.php】中,从而达到攻击的目的。 (0x00是16进制值,不可见。)
2.PHP%00截断
原理与15题类似,使用方法与文件系统0x00截断相同
源码为
1 | <form action="upload.php" method="post" |
后台应该是dir和file连接
先尝试上传一个1.jpg文件
提示上传php文件才行,那上传一个1.php试试
提示不被允许的文件类型,使用burp抓一个上传文件的包,尝试filename处截断,发现上传仍然失败。
尝试在目录处截断,成功。
这个地方尝试截断了好几次,发现直接在重放的Raw中改目录名为”/uploads/1.php0x00”或”/uploads/1.php%00”都没有截断成功,最后是要在Hex中直接改为十六进制的00。
我的做法是先在Raw中现把filename的值“1.php”改为”1.php.jpg”,然后把dir路径改为 /uploads/1.php+
我们知道”+”的十六进制值为2b,所以再去Hex中找到+对应的位置把”2b”改成”00”再重新发包。
传送门:其他文件上传绕过姿势
22.SQL注入1
直接看源码
1 | <?php |
会对传入的参数两端去空格,其中sql构建语句为:select user from ctf where (user='".$user."') and (pw='".$pass."')
直接post个user闭合单引号和括号,再随便传个pass让它存在就可以了。
23.pass check
1 | $pass=@$_POST['pass']; |
之前提过的php弱类型,strcmp函数在比较失败,即传入数组时会返回null
24.起名字真难
1 | <?php |
int ord ( string $string )
函数:返回字符串 string 第一个字符的 ASCII 码值
分析一下代码,这道题需要传入的key参数中不含有1-9的数字但又等于54975581388,考虑编码解决,发现hex(54975581388)刚好等于0xccccccccc
因此访问url:http://chinalover.sinaapp.com/web12/index.php?key=0xccccccccc 就可以拿到flag辣
25.密码重置
tip:重置管理员账号:admin 的密码
burp抓个包看看
放到repeater中把post的user参数改成admin,发现url中get还传了个user1参数,看着像base64编码的,解码发现是ctfuser,所以考虑把user1参数修改为admin经过base64编码后的内容。
26.php反序列化(暂时无法做)
1 | <?php |
反序列化后的secret成员被赋予未知的值却要求另一成员enter其值与之相同
对象包含的引用在序列化时也会被存储,所以如果enter指向secret的引用,两个成员的值就可以同步变化了
在本地写一段代码
1 | <?php |
输出如下:
O:8:"just4fun":2:{s:6:"secret";N;s:5:"enter";R:2;}
所以访问
http://localhost/cgctf2.php?pass=O:8:%22just4fun%22:2:{s:6:%22secret%22;N;s:5:%22enter%22;R:2;}
php反序列化详细的内容我过几天打完ctf去学php代码审计的时候会整理新的博客的!
27.SQL Injection
TIP:反斜杠可以用来转义 仔细查看相关函数的用法
查看源代码
1 | <!-- |
sql语句:SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';
观察clean函数中的返回值经过htmlentities()函数过滤,这个字符将字符转换为 HTML 转义字符 ,第二个参数如果没有默认只转换双引号,但参数值为ENT_QUOTES时查询文档得知既转换双引号又转换单引号。
我们最终目标是平衡单引号,可是经过这个函数过滤我们无法输入单引号,只能想怎么消灭原来的单引号。
构造payload:?username=\&password= or 1#
使得查询语句如下:
1 | SELECT * FROM users WHERE name='\' AND pass=' or 1%23' |
28.综合题
打开后发现是一串看不懂的代码,google一下是jsfuck编码,在线解码网站
直接拉到控制台执行,得到 1bc29b36f623ba82aaf6724fd3b16718.php
打开这个网页得到新的提示
根据提示查看http头信息
上网搜了一下,发现这是一个linux下保存历史命令的文件,默认保存在/root/.bash_history
.bash_history详解:https://yq.aliyun.com/ziliao/75352
所以尝试打开该目录下这个文件 url:http://teamxlc.sinaapp.com/web3/b0b0ad119f425408fc3d45253137d33d/.bash_history
得到新的提示
这是一个linux解压缩的命令,访问新的url:http://teamxlc.sinaapp.com/web3/b0b0ad119f425408fc3d45253137d33d/flagbak.zip可以下载这个压缩文件,用文本编辑器打开该文件得到flag
29.system(暂时无法做)
pass
30.SQL注入2
tip:主要考察union查询
查看关键源代码
1 |
|
strcasecmp(str1, str2):两个字符串相等则返回0
观察一下条件语句 ($query[pw]) && (!strcasecmp($pass, $query[pw]))
mysql_fetch_array() 函数返回的关联数组中键为字段名
构造payload:post一下数据
1 | user=' union select 'bb23bab64934efa2a4e1666109467f43'#&pass=xiangfeng |
先用一个单引号把user闭合,然后联合查询xiangfeng的md5值,这样返回的关联数组中pw建的值就是’bb23bab64934efa2a4e1666109467f43’。
31.综合题2
见另一篇博客:传送门
32.密码重置2
1 | TIPS: |
查看网页源代码得到管理员邮箱:
根据提示,学到一个新知识是非正常关闭vi编辑器时会生成一个.swp文件
查看.index.php.swp和.submit.php.swp文件
能打开.submit.php.swp文件,关键代码如下
1 | if(!empty($token)&&!empty($emailAddress)){ |
要求token长度为10且token!=0为假,有两种绕过方法,第一种传入token=0000000000绕过,第二种利用弱类型(含有数字内容的字符串也会被转换类型)传入token=0e12345678绕过。
33.file_get_contents
1 | <!--$file = $_GET['file']; |
file_get_contents() 函数将整个文件读入一个字符串
这里使用第九题中提到的php伪协议之一:”php://input”可以访问请求的原始数据的只读流,,将post请求中的数据作为PHP代码执行。
34.变量覆盖
代码审计类题目
1 | <!--foreach($_GET as $key => $value){ |
foreach 遍历数组或对象,它会把当前单元的键名也会在每次循环中被赋给变量 $key,值赋给变量$value
$$ 的意思参考可变变量
35. HateIT
这道题太难了!我是跟着网上的writeup一步一步复现的,不然自己根本想不到。
先了解一下web中敏感文件泄露的原因和方法:传送门
扫描发现robots.txt,admin.php,upload/upload.php,.git/
upload/uploa.php不能直接访问,估计是看cookie的
使用 dvcs-ripper 工具将 git 文件下载下来,发现只有一个README.md
提示有历史版本,看来得恢复文件,通过git log
查看记录
通过git reset
回滚版本
拿到了三个通过扩展加密的 php文件和一个txt文件,opcode.txt有index.php,class.php,func.php的 opcode 代码,可以解密这三个php文件。
看一下robots.txt
suenc.so
是一个加密扩展文件,可以下载下来,用来解密admin.php 。
因为我很菜还不会逆向,所以直接贴网上师傅们解密出来的代码了
index.php
1 | <?php |
func.php
1 | <?php/** |
class.php
1 | <?php/** |
admin.php
1 | <?php/** |
先看index.php
,这里会将$token
解密,如果$admin==3
就有权限访问admin.php
,我们在本地生成加密 token
token的加密脚本
1 | <?php |
这个指定admin为3,生成一个token
windows 跟 linux 的随机数是有差异的 ,所以脚本要拉到linux环境中执行。
拿到管理员权限后接着看admin.php
,文件上传好像是因为没有目录权限,一直上传不上东西,就算了。viewImage
调用了ImageView
类,最终通过调用系统命令的方式修改图片大小,这里没有对$filename
进行过滤,直接拼接到命令执行中,构造 payload 执行命令
接下来就是寻找flag之旅了,最后发现flag在/etc/目录下
获得flag!
补充一下审计admin.php源码的过程:
跟进viewImage 跟进ImageView
这里可以命令执行
file的地方就可以传进去命令
36. Anonymous
这道题学到了很有意思的知识!
1 | Anonymous 匿名的 |
根据题目可能是考php匿名函数
先看代码
1 | <?php |
先学习一下匿名函数的创建create_function():
string create_function (string $args, string $cod)
string $args:变量部分
string $code:方法代码部分
一个官方提供的例子:
1 | <?php |
顺便学习一下利用create_function()进行代码注入
1 | <?php |
回到这道题,了解一下本题代码中的几个函数
bin2hex() 函数把 ASCII 字符的字符串转换为十六进制值。
openssl_random_pseudo_bytes 函数根据参数来生成指定个数的随机字节。
die() 函数输出一条消息,并退出当前脚本。
这道题用到的知识点是: 匿名函数的名字和apache的Pre-fork模式
Apache的默认模式Pre-fork会随着请求数量的增加而启动若干新的进程 。
create_function创建的是匿名函数,而匿名函数也是有名字的,名字是\x00lambda_%d,其中%d代表他是当前进程中的第几个匿名函数。%d会从1一直进行递增,表示这是当前进程中第几个匿名函数。因此如果开启一个新的php进程,那么这个匿名函数就是\x00lambda_1,所以思路就是通过向Pre-fork模式的apache服务器发送大量请求,致使apache开启新的进程来处理请求,那么传递func_name=%00lambda_1
就可以执行函数了。
经过了解以上知识,我写了一个超级简单无脑的python脚本跑了一下就跑出来了flag,就是一直传递func_name=%00lambda_1
参数向服务器发起请求,然后直到服务器启动了新的进程这个请求就可以获取flag了。
把url改一下就可以了。这道题改编自HITCON2017的一道web题,只考察了后半部分的知识点,原题orange师傅给出的官方writeup中的poc如下:
fork.py
1 | import requests |
我在本地也复现了一下
步骤如下:
1 | exp: |
总结
终于,经过三天做题两天整理,自己好像摸到了些ctf的门槛,虽然还很菜,关于ctf的下一步可能先去学学隐写术再来刷题。
这些天来学到了很多以前没想过的姿势,也回顾了许多忘了差不多的常见漏洞绕过姿势,都做完之后真的成就感爆棚。
希望能给看这篇文章的你带去一些帮助,要是师傅们发现疏漏了还请快指正我!
教室有些嘈杂,那些嘈杂此时入耳,仿佛成了我心中的白月光。