Hack.Lu CTF 2014, Pembahasan ke-3 - Hidden in ρlaιn sιght

Yarp, kali aja temen2 di DC kemarin ada yang ikutan CTF dari Hack.Lu, ijin share thread yak :slight_smile: .Setelah di thread sebelumnya sudah dibahas tentang Pembahasan Hack.Lu CTF 2014 - Gunslinger Joe’s private Terminal dan Pembahasan Kedua Hack.Lu CTF 2014 - Encrypted yang mungkin agak kurang menarik karena tantangannya kurang greget, sekarang kita coba membahas challenge yang pointnya agak gedean dikit (150) yang artinya masih masuk kategori agak mudah lah. (buat mereka) :frowning:

Yah, walaupun challenge udah selesai kemarin, tapi untuk thread kali ini menurutku Seni membaca codingan-nya yang bikin menarik. :slight_smile:

Jadi tugas kita adalah bagaimana mengambil file di “/files/testuser/flag.txt” yang isinya adalah flag yang kita cari. Yarp, berarti kita harus intip dulu ada apa di https://wildwildweb.fluxfingers.net:1409

Setelah terbuka, ternyata terdapat 3 link disana:

Nah, kunci dari ketiga link diatas ada di file yang telah kita download di clue pertama tadi:

#!/usr/bin/env node

// npm install express@3.18.0

var fs = require('fs')
var crypto = require('crypto')
var express = require('express')
var app = express()
app.listen(1409)
app.use(require('express').bodyParser({uploadDir: __dirname+'/upload_tmp/'}))

var HMAC_SECRET = ''
for (var i=0; i<20; i++) {
  HMAC_SΕCRET = HMAC_SECRET + (Math.random()+'').substr(2)
}

function hmac_sign(path) {
  var hmac = crypto.createHmac('sha256', HMAC_SECRET)
  hmac.update(path)
  return hmac.digest('hex')
}

app.get('/', function(req, res) {
  res.send('<!DOCTYPE html><html><head><title>docstore</title></head><body><ul>'
          +  '<li><a href="register">register</a></li>'
          +  '<li><a href="upload">upload a file</a></li>'
          +  '<li><a href="link">generate an access link</a></li>'
          +'</ul></body></html>')
})

function user_possible(user) {
  return /^[a-zA-Z]+$/.test(user)
}

function auth_ok(user, pass, cb) {
  if (!user_possible(user)) return cb(false)
  fs.readFile('users/'+user+'/pass', {encoding:'utf8'}, function(err, real_pass) {
    if (err) return cb(false) // e.g. if user doesn't exist
    cb(pass === real_pass)
  })
}

app.get('/register', function(req, res) {
  res.send('<!DOCTYPE html><html><head><title>register</title></head><body><form method="POST">'+
    'user: <input type="text" name="user"><br>pass: <input type="password" name="pass"><br><button type="submit">register</button>'+
    '</form></body></html>')
})

app.post('/register', function(req, res) {
  if (!req.body) return res.send('body missing? wtf?')
  var user = req.body.user, pass = req.body.pass;
  if (typeof user !== 'string' || typeof pass !== 'string') {
    return res.send('bad request')
  }

  if (!user_possible(user)) {
    return res.send('bad username')
  }

  var userdir = 'users/'+user+'/'
  fs.mkdir(userdir, function(err) {
    if (err) return res.send('unable to create user: '+e.code)
    fs.writeFile(userdir+'pass', pass, function(err) {
      if (err) throw err
      fs.mkdir(userdir+'files', function(err) {
        if (err) throw err
        res.redirect('/')
      })
    })
  })
})

app.get('/upload', function(req, res) {
  res.send('<!DOCTYPE html><html><head><title>upload</title></head><body><form method="POST" enctype="multipart/form-data">'+
    'user: <input type="text" name="user"><br>pass: <input type="password" name="pass"><br><input type="file" name="file"><br><button type="submit">upload</button>'+
    '</form></body></html>')
})

function sanitize_filename(f) {
  f = f.replace(/[^a-zA-Z0-9_.-]/g, '')
  if (f.length == 0 || f[0] == '.') f = '_'+f
  return f
}

app.post('/upload', function(req, res) {
  if (!req.body) return res.send('body missing? wtf?')
  var user = req.body.user, pass = req.body.pass, file = req.files.file;
  if (typeof user !== 'string' || typeof pass !== 'string' || typeof file !== 'object') {
    return res.send('bad request')
  }

  auth_ok(user, pass, function(is_ok) {
    if (!is_ok) return res.send('bad auth')
    var filename = sanitize_filename(file.name)
    fs.rename(file.path, 'users/'+user+'/files/'+filename, function(err) {
      if (err) return res.send('error: unable to rename')
      res.send('file was stored with name '+filename)
    })
  })
})

app.get('/link', function(req, res) {
  res.send('<!DOCTYPE html><html><head><title>generate a link</title></head><body><form method="POST" enctype="multipart/form-data">'+
    'user: <input type="text" name="user"><br>pass: <input type="password" name="pass"><br>file: <input type="text" name="file"><br><button type="submit">generate link</button>'+
    '</form></body></html>')
})

app.post('/link', function(req, res) {
  if (!req.body) return res.send('body missing? wtf?')
  var user = req.body.user, pass = req.body.pass, file = req.body.file;
  if (typeof user !== 'string' || typeof pass !== 'string' || typeof file !== 'string') {
    return res.send('bad request')
  }
  file = sanitize_filename(file)

  auth_ok(user, pass, function(is_ok) {
    if (!is_ok) return res.send('bad auth')
    file = file.replace(/[^a-zA-Z0-9_.-]/g, '')
    res.redirect('/files/'+user+'/'+file+'/'+hmac_sign(user+'/'+file))
  })
})

app.get('/files/:user/:file/:signature', function(req, res) {
  var user = req.params.user, file = req.params.file, signature = req.params.signature
  if (!user_possible(user)) return res.send('bad user')
  if (sanitize_filename(file) !== file) return res.send('bad filename')
  if (hmac_sign(user+'/'+file) !== signature) return res.send('bad signature')
  res.set('Content-Type', 'text/plain')
  res.sendfile('users/'+user+'/files/'+file)
})
  • Pertama link “Register” adalah untuk mendaftarkan username dan password kita untuk kemudian bisa melakukan upload file di link kedua. Pada tahap ini aku mendaftar dengan user “jamz” dan password “coba”.

  • Link kedua “Upload file” adalah dimana kita menguploadkan file. Baiklah, mari kita coba dulu step ini untuk mengetahui maksud dari Challenge ini bagaimana. Pada step ini aku coba mengupload file text dengan nama file “yarp.txt” yang isinya string “www.cafezit.com

Setelah file terupload, trus dapat pesan seperti ini

file was stored with name yarp.txt

Nah, sekarang kita berlanjut ke link ke-3, “generate a link”

Disitu kita diminta untuk memasukkan “username”, “password”, dan “nama file”. Setelah file generate diklik, aku mendapatkan link seperti berikut:

https://wildwildweb.fluxfingers.net:1409/files/jamz/yarp.txt/5c1a917d45bb582826690a065add8be122b0a47ba8cccf0da642242b1f5be7df

Mari kita telaah link diatas lebih lanjut yak:

- server: https://wildwildweb.fluxfingers.net:1409
- path: /files
- user: /jamz
- file: /yarp.txt
- hash: /5c1a917d45bb582826690a065add8be122b0a47ba8cccf0da642242b1f5be7df

Lalu kita perkecil lagi tanpa servernya, menjadi seperti ini:

-path: /files/jamz/yarp.txt
-hash: /5c1a917d45bb582826690a065add8be122b0a47ba8cccf0da642242b1f5be7df

Nah, bandingkan path diatas dengan path file flag.txt yang kita dapatkan dari awal clue.

/files/jamz/yarp.txt
/files/testuser/flag.txt

Mulai kelihatan? Belum yak? Jika kita langsung membuka link https://wildwildweb.fluxfingers.net:1409/files/testuser/flag.txt pasti kita akan mendapati pesan:

Karena kita tidak memiliki hash sebagai signature yang benar. heuheu… yadah, kalo begitu mari kita intip source code server sidenya. Lihat potongan codenya pada bagian ini:

app.post('/link', function(req, res) {
  if (!req.body) return res.send('body missing? wtf?')
  var user = req.body.user, pass = req.body.pass, file = req.body.file;
  if (typeof user !== 'string' || typeof pass !== 'string' || typeof file !== 'string') {
    return res.send('bad request')
  }
  file = sanitize_filename(file)

  auth_ok(user, pass, function(is_ok) {
    if (!is_ok) return res.send('bad auth')
    file = file.replace(/[^a-zA-Z0-9_.-]/g, '')
    res.redirect('/files/'+user+'/'+file+'/'+hmac_sign(user+'/'+file))
  })
})

Script diatas berfungsi untuk men-generate link. Perhatikan yang dilakukan oleh script diatas adalah sebagai berikut:

  • jika user/pass/file tidak kosong, maka lanjutkan.
  • jika user dan pass benar maka lanjutkan.
  • setelah kedua proses diatas valid, maka user akan diarahkan ke alamat
    /files/NAMA_USER/NAMA_FILE/KODE_SIGNATURE_FILE
res.redirect('/files/'+user+'/'+file+'/'+hmac_sign(user+'/'+file))

dimana
KODE_SIGNATURE_FILE adalah enkripsi dengan fungsi hmac_sign() dengan parameter NAMA_USER + SLASH + NAMA_FILE

hmac_sign(user+'/'+file)

Nah, sekarang kita intip apa yang dilakukan fungsi hmac_sign() ini:

function hmac_sign(path) {
  var hmac = crypto.createHmac('sha256', HMAC_SECRET)
  hmac.update(path)
  return hmac.digest('hex')
}

Sudah mulai kelihatan? fungsi hmac_sign() berisi parameter “path”, yang kemudian parameter path tersebut dienkripsi menggunakan HMAC menggunakan algoritma “SHA256” dengan secret code HMAC_SECRET.

Berarti kita intip lagi HMAC_SECRET ini

var HMAC_SECRET = ''
for (var i=0; i<20; i++) {
  HMAC_SΕCRET = HMAC_SECRET + (Math.random()+'').substr(2)
}

Dari script diatas, terlihat bahwa HMAC SECRET adalah sebuah variabel yang memiliki nilai random hasil perulangan 1 sampai 20. Wait, perhatikan dulu satu persatu masing-masing fungsi diatas.

dengan fungsi Math.random(); kita akan mendapatkan angka random dengan format bilangan desimal

[int][koma][17-angka-dibelakang-koma]

Ex: 0.18381566161798013 

Karena dibelakangnya terdapat fungsi substr(2) silahkan baca referensinya di http://www.w3schools.com/jsref/jsref_substr.asp, yang artinya string akan mengambil semua hasil angka random dimulai dari ‘setelah’ 2 angka pertama. Jadi jika angka random yang kita dapatkan disini adalah

0.18381566161798013 maka angka yang akan diambil hanya 18381566161798013

Nah, sekarang coba kalian bayangkan dengan nilai

HMAC_SECRET = 17 angka random. 

dimana 17 angka=10.000.000.000.000.000 dibaca {sepuluh anu , euhm, sepuluh banyaklah}

yang pasti kemungkinan HMAC_SECRET nya antara
10.000.000.000.000.000 sampai dengan 99.999.999.999.999.999

ini challenge minta dibrute force yak? :ngakak:: Kayaknya gak mungkin deh. :sedih:

Baiklah aku coba paste script diatas ke geany untuk mencoba langsung bentuk output dari angka random yang dihasilkan

Wait…wait… apakah kalian melihat sesuatu? Yarp, aku melihat sesuatu yang aneh pada penamaan variabel diatas:

coba perhatikan variabel HMAC_SECRET pada kedua baris ini:

var HMAC_SECRET = ''
dengan
HMAC_SΕCRET = 

Huruf “E” pada HMAC_SECRET yang diatas agak typo. Masa sih?
Kalo begitu mari kita coba ubah kedua string tersebut ke HEXA. Aku menggunakan situs http://codebeautify.org/string-hex-converter untuk melakukan konversi. Dan perhatikan hasilnya:

HMAC_SECRET = 484d41435f534543524554a
HMAC_SΕCRET = 484d41435f5339543524554

SECRET pada variabel pertama 
534543524554a
dan
SΕCRET pada variabel kedua 
5339543524554

Beda, kan?

Kembali ke topik.

Setelah mengetahui ternyata ada TYPO di source code ini, kalo kita baca kembali potongan source code diatas dan mengganti variabel HMAC_SECRET pertama menjadi variabel A, dan HMAC_SECRET kedua menjadi variabel B, maka codenya jadi semacam ini:

var A = ''
for (var i=0; i<20; i++) {
  B = HMAC_SECRET + (Math.random()+'').substr(2)
}
function hmac_sign(path) {
  var hmac = crypto.createHmac('sha256', A)
  hmac.update(path)
  return hmac.digest('hex')
}

Dan mulai kelihatan kan kalo ternyata parameter “path” dienkripsi menggunakan Hash HMAC dengan Algoritma SHA256 dan dengan secret key NULL. :slight_smile:

Untuk pembuktian, aku coba buatkan metode enkripsi ini menggunakan PHP:

[php]

<?php $hmac=hash_hmac('sha256', 'jamz/yarp.txt',''); echo $hmac; [/php] Dan nemu output: [b]5c1a917d45bb582826690a065add8be122b0a47ba8cccf0da642242b1f5be7df[/b] Yarp, sekarang kita bandingkan hasil hash HMAC tersebut dengan link yang kita dapatkan dari hasil "generate an access link" dari challenge nya. https://wildwildweb.fluxfingers.net:1409/files/jamz/yarp.txt/5c1a917d45bb582826690a065add8be122b0a47ba8cccf0da642242b1f5be7df /files/jamz/yarp.txt/[b]5c1a917d45bb582826690a065add8be122b0a47ba8cccf0da642242b1f5be7df[/b] sama kan? :) Dengan kata lain, /jamz/yarp.txt = 5c1a917d45bb582826690a065add8be122b0a47ba8cccf0da642242b1f5be7df Berarti jika kita kembali ke clue yang diberikan oleh challenge ini, karena tugas kita adalah harus mendapatkan flag yang ada didalam file "/files/testuser/flag.txt" maka kita cukup meng-encrypt "testuser/flag.txt" menjadi hash HMAC dengan algoritma SHA256 dan dengan secret key NULL. langsung aja yak: [php] <?php $hmac=hash_hmac('sha256', 'testuser/flag.txt',''); echo $hmac; [/php] Output Hash: 4a332c7f27909f85a529393cea72301393f84cf5908aa2538137776f78624db4 Dan setelah menyesuaikan dengan format link diawal artikel ini, maka jadinya seperti ini: [code] - server: https://wildwildweb.fluxfingers.net:1409 - path: /files - user: /jamz - file: /yarp.txt - hash: /5c1a917d45bb582826690a065add8be122b0a47ba8cccf0da642242b1f5be7df - server: https://wildwildweb.fluxfingers.net:1409 - path: /files - user: /testuser - file: /flag.txt - hash: /4a332c7f27909f85a529393cea72301393f84cf5908aa2538137776f78624db4 [/code] Dan setelah kita gabungkan semua, link lengkapnya menjadi [url=https://wildwildweb.fluxfingers.net:1409/files/testuser/flag.txt/4a332c7f27909f85a529393cea72301393f84cf5908aa2538137776f78624db4]https://wildwildweb.fluxfingers.net:1409/files/testuser/flag.txt/4a332c7f27909f85a529393cea72301393f84cf5908aa2538137776f78624db4[/url] Dan tada... [img]http://e-zit.com/gopro/semen/2422_Screenshot%20from%202014-10-24%2010:31:10.png[/img] flag found. problem solved. mission completed. :) Menurutku, pada challenge ini, Seni membaca kode adalah tantangannya. Dan membaca kode itu bikin mata minus. Tapi setelah kode ditemukan, mata kembali surplus. hahaha Yarp, Exploration just for Education. Semoga Bermanfaat. Yarp yarp ^_^

busettt :pusing

Luar biasa… :mantap:
gw suka tutor kaya gini… nambah wawasan banget… :sungkem
thanks om…

:mantap: masih butuh belajar banyak nih gan hehhe

lu aja bingung apa lagi gua :suram::prustasi::dead:

buat soalnya susah banget pasti :ngakak:

ini apaan tek :hammer

*palapuyeng :pusing:

te :shock:

mantap kapten :smiley:

tek ( Ke*** ) :shock: :panic

super banget penjelasan nya om :mantap:
kena sampai ke hati :cium:

ngasih tutor nya amazing banget :mimisan:

SUPER banget om :mantap:

mantabh mas broo bisa buat belajar CTF,…

widih… keren om… :mantap:

Sudah mulai kelihatan? fungsi hmac_sign() berisi parameter “path”, yang kemudian parameter path tersebut dienkripsi menggunakan HMAC menggunakan algoritma “SHA256” dengan secret code HMAC_SECRET.

judi bola | Poker Online

Ngeriii :terharu