2019/11/25

Identificando versiones de programas

Supongamos que tengo que hallar versiones viejas de programas en una red y no puedo ejecutar:
 

$ programa --version

pues no puedo entrar a cada máquina, aunque si puedo ver su sistema de archivos. Tampoco hay un gestor de aplicaciones al cual consultar, incluso pueden haber programas instalados sin el uso de un usuario privilegiado, como puede ser un portable o compilado localmente.




Supongamos que tenemos el ejecutable identificado, como ejemplo usaré chrome.


En linux Mint y supongo Ubuntus está en:

/opt/google/chrome/chrome

En windows lo he hallado en lugares como:


C:\Program Files (x86)\Google\Chrome\Application\
C:\Users\XXXXX\AppData\Local\Google\Chrome\Application\


Primero hay que tener varias versiones identificadas:

chrome.lin.78.0.3904.97
chromium-browser.lin.65.0.3325.181
chrome.win32.24.0.1312.57
chrome.win.44.0.2403.155
 

chrome.win.51.0.2704.84
chrome.win.57.0.2987.133
chrome.win.62.0.3202.75 
chrome.win.78.0.3904.97

Las de linux se obtienen descomprimiento el deb que se consigue en  

http://security.ubuntu.com/ubuntu/pool/universe/c/chromium-browser/

¡Qué féo ese http sin la s!

Con grep vamos viendo si esos números están almacenados. 


$ grep 3904 chrome.*
Binary file chrome.lin.78.0.3904.97 matches
Binary file chrome.win.78.0.3904.97 matches

con -a le decimos a grep que los tome como archivos de texto pese a que son binarios. De pado limitamos el contexto de match con -o y con ".{32}".

$ grep -a -o '.\{32\}78\.0\.3904\.97.\{32\}' chrome.*

chrome.lin.78.0.3904.97:brtc/pc/peer_connection.cc:609778.0.3904.97id-smime-mod-ets-eSigPolicy-97
 
chrome.win.78.0.3904.97:test-child-processvvmodule78.0.3904.97GCM Store
 
chrome.win.78.0.3904.97:mblyIdentity type="win32" name="78.0.3904.97" version="78.0.3904.97" languag

Mmmm, ese último me gustó, probemos con algo más específico y sin -o



$ grep -a 'version="78.0.3904.97"' chrome.*
chrome.win.78.0.3904.97:<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><dependency><dependentAssembly><assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity></dependentAssembly></dependency><dependency><dependentAssembly><assemblyIdentity type="win32" name="78.0.3904.97" version="78.0.3904.97" language="*"></assemblyIdentity></dependentAssembly></dependency><trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel></requestedPrivileges></security></trustInfo><compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"><application><supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"></supportedOS><supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"></supportedOS><supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS><supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"></supportedOS><supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"></supportedOS></application></compatibility></assembly>( ;J�=I�=I�?L�@L�AM�BM�DP�AN�L]�DO�BO�CO�DP�EQ�FR�DR�GR�GS�OwdHT�IT�IU�Qo�IV�LW�Q\�Wb�[� [� >e�@e�]� ?f�[� \� >f�@g�]� ]�8]� Z�#^� _�[]� _� _� _� a�(`� `� a� b� b� c� ��Ie�!g�)��Jd�!��L��Mi�)Su�n�/��YZ�A��[��^P��J��O��<��?���ç�Ĩ�ĩ�Ū>��������D���Ѱ[��E��@���ձA��B��A��B��C��@��A��B��C��D��E��R��S��������������hhhhhhhhhhhhhhhhhhhhhh BQUhhhhhhhhhh FZYWhhhhhhh,&&#")TZY\hhhhh-**((>9E[ZYXhhhh//.@VJJRS[ZYhhh2113dC:7Af`[Z]hh455+L<::7Jc[Z]hh;60 L=<::Jb`[^hh;8 PD=<Cgaa_]hhh'
 
Me gusta cada vez más, quedémonos con la parte interesante de ese xml con identación para mejorar la lectura:

<assembly ...
   <assemblyIdentity
      type="win32"
      name="78.0.3904.97"
      version="78.0.3904.97"
      language="*"
   >
   </assemblyIdentity>
...
</assembly>

Y repitamos para las otras versiones con un nuevo grep ajustado:


$ grep -a -o "<assemblyIdentity type=\"win32\" name=\".\{16\}" chrom*
chrome.win.44.0.2403.155:<assemblyIdentity type="win32" name="44.0.2403.155" v
chrome.win.51.0.2704.84:<assemblyIdentity type="win32" name="51.0.2704.84" versio
chrome.win.57.0.2987.133:<assemblyIdentity type="win32" name="Microsoft.Window
chrome.win.57.0.2987.133:<assemblyIdentity type="win32" name="57.0.2987.133" v
chrome.win.62.0.3202.75:<assemblyIdentity type="win32" name="Microsoft.Window
chrome.win.62.0.3202.75:<assemblyIdentity type="win32" name="62.0.3202.75" ve
chrome.win.78.0.3904.97:<assemblyIdentity type="win32" name="Microsoft.Window
chrome.win.78.0.3904.97:<assemblyIdentity type="win32" name="78.0.3904.97" ve


Se me cayó linux, luego vemos. Para windows con esta regla ya estamos:

  • Si tiene información de versión
  • Y esa información no coincide con la versión que deseo

Dicho en yara:


rule ChromeWin
{
  strings:
    $version   = "<assemblyIdentity type=\"win32\" name=\""
    $version78 = "<assemblyIdentity type=\"win32\" name=\"78."
  condition:
    $version and not $version78
}

y su ejecución:

$ yara ruleChrome.yara .
ChromeNot78 ./chrome.win.51.0.2704.84
ChromeNot78 ./chrome.win.44.0.2403.155
ChromeNot78 ./chrome.win.57.0.2987.133
ChromeNot78 ./chrome.win.62.0.3202.75

Y así tenemos un modo que aunque dependiente del programa es independiente de las versiones menores y revisiones para identificarla estáticamente. Tambien pudimos usar un hash, pero hace falta identificar todos los casos.

Si sólo dejamos $version78, igual funciona, pero si hay otros programas los halla tambien.

Uh, uh, qué difícil, no? 

Quizás con linux suframos un poco más, veamos que tenemos:

$ grep -a -o ".\{16\}65.0.3325.181.\{16\}"chromium-browser.lin.65.0.3325.181
lurl_template65.0.3325.181BREAKPAD_DUMP_L

$ grep -a -o ".\{16\}78.0.3904.97.\{16\}" chrome.lin.78.0.3904.97

nection.cc:609778.0.3904.97id-smime-mod-e
 
Está en el medio de la nada, ni siquiera es un null terminated string, nos puede servir hallar en que section se encuentra, comenzando por el offset en el archivo:


$ hexdump -C chrome.lin.78.0.3904.97 | grep 3904
01039040  2f 76 69 64 65 6f 5f 62  69 74 72 61 74 65 5f 61  |/video_bitrate_a|
010db670  37 38 2e 30 2e 33 39 30  34 2e 39 37 00 69 64 2d  |78.0.3904.97.id-|
01139040  64 65 66 69 6e 65 64 2e  00 72 65 71 75 69 72 65  |defined..require|

Ahora hay que buscar donde cae el offset 010db670, tiene que estar en la zona amarilla, en realidad en la sección anterior a la valor inmediatamente mayor.
 
$ readelf -S --wide chrome.lin.78.0.3904.97
There are 38 section headers, starting at offset 0x91713c8:

Section Headers:
[Nr] Name              Type      Address    Off    Size   Flg
[ 0]                   NULL      00000000  000000  000000  
[ 1] .interp           PROGBITS  000002e0  0002e0  00001c   A
[ 2] .note.ABI-tag     NOTE      000002fc  0002fc  000020   A

[ 3] .note.gnu.build-id NOTE     0000031c  00031c  000024   A
[ 4] .dynsym           DYNSYM    00000340  000340  009ab0   A
[ 5] .gnu.version      VERSYM    00009df0  009df0  000ce4   A
[ 6] .gnu.version_r    VERNEED   0000aad4  00aad4  0003b0   A
[ 7] .gnu.hash         GNU_HASH  0000ae88  00ae88  0001dc   A
[ 8] .dynstr           STRTAB    0000b064  00b064  007ab9   A
[ 9] .rela.dyn         RELA      00012b20  012b20  e16a68   A
[10] .rela.plt         RELA      00e29588  e29588  009210   A
[11] .rodata           PROGBITS  00e32800  e32800  c047bf AMS
[12] protected_memory  PROGBITS  01a36fc0 1a36fc0  0001f8   A

[13] .gcc_except_table PROGBITS  01a371b8 1a371b8  001aac   A
[14] .eh_frame_hdr     PROGBITS  01a38c64 1a38c64  007c44   A
[15] .eh_frame         PROGBITS  01a408a8 1a408a8  01c464   A
[16] .text             PROGBITS  01a5d000 1a5d000 7028912  AX
[17] .init             PROGBITS  08a85914 8a85914  000017  AX
[18] .fini             PROGBITS  08a8592c 8a8592c  000009  AX
[19] malloc_hook       PROGBITS  08a85940 8a85940  000556  AX
...

Key to Flags:
 W (write), A (alloc), X (execute), M (merge), 

 S (strings), l (large),  I (info), L (link order),
 G (group), T (TLS), E (exclude), x (unknown),
 O (extra OS processing required),
 o (OS specific), p (processor specific)

Lo cual es razonable pues hasta donde entiendo los strings como este en .rodata deben estar.


$ objdump -s -j .rodata chrome.lin.78.0.3904.97 \
 | grep 3904 -A 2 -B 2 

10db650 62727463 2f70632f 70656572 5f636f6e  brtc/pc/peer_con
10db660 6e656374 696f6e2e 63633a36 30393700  nection.cc:6097.
10db670 37382e30 2e333930 342e3937 0069642d  78.0.3904.97.id-
10db680 736d696d 652d6d6f 642d6574 732d6553  smime-mod-ets-eS
10db690 6967506f 6c696379 2d393700 69642d73  igPolicy-97.id-s


más arriba cuando había dicho ni siquiera es un null terminated string me equivoqué, ahí está clarito el null.
Pero esto seguro cambia de versión en versión.

$ objdump -s -j .rodata chromium-browser.lin.65.0.3325.181 \
 | grep 65\.0\.3325 -A 2 -B 2

6d32960 00636872 6f6d6500 7469746c 65007465  .chrome.title.te
6d32970 78740075 726c0075 726c5f74 656d706c  xt.url.url_templ
6d32980 61746500 0036352e 302e3333 32352e31  ate..65.0.3325.1
6d32990 38310042 5245414b 5041445f 44554d50  81.BREAKPAD_DUMP
6d329a0 5f4c4f43 4154494f 4e005061 796d656e  _LOCATION.Paymen



Ok, quizás buscando un string de cuatro enteros separados por puntos entre dos nulos funcione... demasiado, trae toda dirección IP



$ grep -a -o -P \
 "\x00[1-9][0-9]*\.[0-9]+\.[0-9]+\.[0-9]+\x00" \
  chromium-browser.lin.65.0.3325.181  \
  chrome.lin.78.0.3904.97

chromium-browser.lin.65.0.3325.181:6.5.254.41
chromium-browser.lin.65.0.3325.181:65.0.3325.181
chromium-browser.lin.65.0.3325.181:239.255.255.250
chromium-browser.lin.65.0.3325.181:127.0.0.1
chrome.lin.78.0.3904.97:78.0.3904.97
chrome.lin.78.0.3904.97:7.8.279.23
chrome.lin.78.0.3904.97:4.10.1582.1
chrome.lin.78.0.3904.97:127.0.0.1
chrome.lin.78.0.3904.97:224.0.0.251
chrome.lin.78.0.3904.97:239.255.255.250
chrome.lin.78.0.3904.97:127.0.0.1

¡Qué problema! El orden no significa nada, sólo podríamso descartar algunas IPs muy evidentes como  127.0.0.1 y 239.255.255.250,pero nos empieza a obligar a tener una lista de versiones más estrictas.

No es tan terrible. para cada programa candidato, obtenemos la lista de strings potenciales y si alguna coincide con versiones correctas o incorrectas. 

Esta parte de linux ha sido para practicar lo aprendido en el libro Practical Binary Analysis, cuya reseña en pocas semanas publicaré.



No hay comentarios:

Publicar un comentario