2019国赛西南赛区部分WEB WP

2019国赛西南赛区部分WEB WP

前言

  • 国赛区域赛打完了,太菜了,就当公费旅游了。趁这几天有空复现了一下几道题。

day1

  • 划水并且上别人的车。源码忘了拷,血亏。只做出一道yml反序列化的题,还是上的别人的车。

day2 web1

  • 先膜一发evoA师傅,感谢大师傅给的思路。
  • 这题思路很骚,虽然做题点都学过,但是思路很巧妙。 考点:htaccess文件写入
  • 首先,提示是文件上传,但是文件上传处被过滤的死死的;整个目录中相对宽松的地方就只有login-lock.php处的代码了,通过post time参数可以写入任意代码:
    1
    2
    3
    4
    5
    6
    7
    8
    if (isset($_POST["time"])) {
    $time = $_POST["time"];
    $lock = fopen("files/" . $_SESSION["name"], "w");
    fwrite($lock, $time);
    fclose($lock);
    sleep(2);//假装处理要消耗不少时间 让动画好看一点
    echo "files/" . $_SESSION["name"];
    }

但是,生成的文件名使用的name是用户名,而在注册用户时,用户名过滤比较严格:

1
preg_match('/(.+)(?=\.)/', $name, $matches); //只要发现.前面有东西就干死

其实回过头来才看出来作者是在提示:.前面有东西就干死 :),那前面没东西不就好了。
所以这里可以创建一个用户名为.htaccess的用户,在其中将jpg文件解析为php;然后再创一个正常用户,上传一个图片马即可getshell(不能用.htaccess用户是因为php默认不解析包含htaccess关键字的文件,而上传的图片名也为用户名)
.htaccess内容为:

1
AppType application/x-httpd-php .jpg

一个意外发现的点

  • 这个点是队友想出来的,既然可以直接写入没有文件名而只有后缀的文件,是不是可以直接写入.php文件呢?
  • 在不同的系统上尝试,发现在本机的windows中可以直接解析.php文件,而在ubuntu系统中则无法解析。
  • 原因是apache在linux下解析文件时,对隐藏文件(开头为.号)的文件是不给访问权限的,所以无法直接写入.php文件getshell。

day2 web2

  • 此题源码泄露+多行匹配绕过
  • 首先扫描路径获得swp文件(做题时自己字典太菜了,居然没扫到,很难受),还原得源码,关键代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <?php
    error_reporting(0);
    $file_name = $_POST["path"];
    if(preg_match("/flag/",$file_name)){
    die("请不要输入奇奇怪怪的字符!");
    }
    if(is_array($file_name)){
    $file_name=implode($file_name);
    }
    if(!preg_match("/^[a-zA-Z0-9-s_]+.rar$/m", trim($file_name))) {
    echo trim($file_name).'<br>';
    echo "请输入正确的文件名";
    }else {
    echo "/usr/bin/md5sum " . $file_name;
    echo( exec("/usr/bin/md5sum " . $file_name));
    }
    ?>

  • 可以看到有命令执行,第一处的preg_match用传入数组绕过,第二处的preg_match用多行匹配%0a绕过(多行匹配对每一行进行匹配,只要有一行满足就返回1)。 payload:

    1
    path[]=1.rar%0acat /flag

day2 web3

  • 吐槽一下,这题是真的辣鸡,出题人出错了题,主办方也不好好审一下。
  • 比赛的时候一眼看出来是南邮CTF的一道题的改版,直接拿当时的脚本怼,不出结果。再查看注释,发现要输入一个比 1000000大的最小的素数,查了一下是1000003,拿脚本继续跑,各种改,跑了一上午都不出flag;这时候看看全场,居然没有一支队伍做出来,肯定遇到坑了,就没做了。
  • 最后fix环节的时候才发现题给错了,应该是比 10000000大的最小的素数,为 10000019。拿脚本又打了一遍,出flag了,手动微笑,建议把出题的打死 :)
  • 此题几乎照搬NCTF“小绿草之最强大脑”,考点:会写脚本解析网页,直接上脚本:
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#calc.py
import re
def jia(*args):
'''
加法解析
:param args:
:return:
'''
# print('jia:', args)
res = 0
for i in args:
#解决*-和/-的情况
#i = i.replace('*-','#').replace('/-','@')
jian_list = i.split('-')
# 减号的拆分要解决负号的情况:把负号拆分后列表中所有为空字符串的元素替换成'0'
# 还要把*-和/-替换回来
for id,j in enumerate(jian_list):
if j=='':
jian_list[id] = '0'
else:
jian_list[id] = j.replace('#','*-').replace('@','/-')
res += jian(*jian_list)
return res
def jian(*args):
'''
减法解析
:param args:
:return:
'''
# print('jian:', args)
#减法需要先算出第一个式子,拿第一个式子去减后面的式子
res = args[0:1]
res = chen(*res[0].split('*'))
for i in args[1:]:
chen_list = i.split('*')
res -= chen(*chen_list)
return res
def chen(*args):
'''
乘法解析
:param args:
:return:
'''
# print('chen:',args)
res = 1
for i in args:
chu_list = i.split('/')
res *= chu(*chu_list)
return res
def chu(*args):
'''
除法解析
:param args:
:return:
'''
# print('chu:', args)
#除法需要先取出第一个,拿第一个除以后面的
res = args[0:1]
res = int(res[0])
for i in args[1:]:
i = int(i)
res /= i
return res
def simpleCalc(input_str):
'''
不带括号的表达式解析
:param input_str:
:return:
'''
# 去掉最外层的括号
input_str = input_str.strip('()')
# 处理 --、+- 的情况,还有 *-、/- 的情况没处理
input_str = input_str.replace(' ','').replace('--','+').replace('+-','-').replace('*-','#').replace('/-','@')
#print(input_str)
#计算加减乘除
jia_list = input_str.split('+')
res = jia(*jia_list)
return res
#return str(eval(input_str))
def calc(input_str):
'''
计算器入口
:param input_str:
:return:
'''
if len(input_str) == 0:
# print('Wrong input')
exit(0)
#print(input_str)
#查找是否还有括号
m = re.search('\([^()]+\)', input_str)
brackets_exists = False
#print(m)
if m == None:#不再有括号了,就直接计算
simple_calc_str = input_str#需要计算的值的字符串就是传入的表达式
else:#还有括号,就把找到的括号中的表达式计算的值替换括号所在位置
brackets_exists = True
simple_calc_str = m.group()#需要计算值的字符串是找到的括号中的表达式
simple_res = str(simpleCalc(simple_calc_str))
if brackets_exists:#还有括号,就把找到的括号中的表达式计算的值替换括号所在位置,进入迭代
return calc(input_str.replace(simple_calc_str,simple_res,1))
else:#没有括号就直接把计算结果返回
return simple_res
if __name__ == '__main__':
# input_str = '3 * 4 + (-4 / 2 - 8 - 3 * 2 + ( 4 - 5 / 2 + 11 - ( 2 * 3 - 9 ) - 12 )) + 20 - 3 * 2 - ( 5 + 8 / 4)'
# input_str = '3/(-1) - (4*-2)/(1+1)/(1+1)'
input_str = '1-2*30000000000000000000000000000000000000000'
result = calc(input_str)
print(result)
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
#get_flag.py
import requests
import re
from calculator import calc
from bs4 import BeautifulSoup
import time
url="http:/localhost:801/ctf_test/guosai_xx/source/web3//index.php"
sess=requests.session()
res = sess.post(url)
for i in range(5):
vstr = res.text
soup = BeautifulSoup(vstr,'lxml')
form = soup.text
form = form.strip('\n')
form = form.strip('\r')
# form = form.replace('\t','')
print('原:',form) # 原
form = ''.join(re.findall('([0-9\-*+])', form))
print(form) # 原
if(i!=0):
form=form[1:]
form=form[1:]
print('提取算式,剪切',form) # 提取算式,剪切
form = '10000019+(' + form+')' #1000003
print('算式'+form)
result = calc(form)
print(result)
data={'input': '10000019',
'ans': result}
time.sleep(2)
res = sess.post(url,data=data)
# print(res.text)
print(res.headers)
print(res.text)
print(res.headers)

结语

  • 第一天还有很多道题没来得及拷出来,不知道能不能在网上找到WP;自己的水平也很菜,继续学习才能赶上大部队。
  • 最后附上本文中题的源码:https://github.com/HACHp1/hackable_cms_store/tree/master/guosai_xx