Assalamualaikum warahmatullahi wabarakatuh, halo teman-teman pada artikel kali ini akan cukup singkat saja pembahasannya, karena ini akan menjadi bentuk dokumentasi pribadi saya yang sempat terlupa untuk beberapa saat. Jadi ceritanya ada sebuah grup di Whatsapp yang sudah lama tidak aktif dan tiba-tiba ada diskusi saya cukup panjang, saya sempat melontarkan chat singkat “Tumben ada diskusi”. Setelah scroll cukup panjang ke atas tiap-tiap diskusinya saya menemukan pertanyaan seperti ini.
Jadi setelah membaca beberapa diskusi setelah chat itu berada, saya dapat menyimpulkan ada sebuah pertanyaan tentang cara memonitor sebuah request dari sebuah web yang sudah di-deploy tanpa merubah script yang ada dan juga tanpa menggunakan proxy apapun aka Nginx / HAproxy dan sejenisnya. Respon pribadi yang muncul dalam diri saya adalah “Hmm, apakah memang bisa?” tapi sejenak saya berifikir kembali apa itu Teminologi HTTP/s Request & Response yang hanya sebuah TCP format tertentu saja dan respon pribadi saya menjadi “Pasti bisa sih, ez”. Dari hasil diskusi yang ada mari kita mocking kejadian yang sedang dialami oleh penanya (saya tidak kenal beliau). Aplikasi web dibangun dengan golang/rust/nodejs atau bahasa apapun yang mempunyai HTTP server sendiri diserve langsung tanpa proxy dan dengan case menerima sebuah request.
Saya menyiapkan sebuah aplikasi Golang yang akan melakukan serving HTTP Server dan Endpoint dengan module net/http
akan ada 2 endpoint yang ada, satu melakukan handling untuk GET parameter dan satu lagi akan melakukan handling JSON data dari POST method.
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", helloWorldHandler)
http.HandleFunc("/post", postHandler)
fmt.Println("Server starting on port 8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func helloWorldHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("Hello World"))
}
func postHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Unable to read request body", http.StatusBadRequest)
return
}
defer r.Body.Close()
var data map[string]interface{}
if err := json.Unmarshal(body, &data); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
response := map[string]interface{}{
"status": 200,
"msg": "ok",
}
responseData, err := json.Marshal(response)
if err != nil {
http.Error(w, "Unable to marshal response JSON", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(responseData)
}
Setelah dijalankan kita akan belajar secara singkat apa itu Three-Way Handshake yang mana menjadi sebuah proses yang digunakan oleh TCP untuk membentuk koneksi antara server dan client sebelum melakukan Terminologi HTTP/s. Dalam konsep Three-Way Handshake akan ada langkah-langkah sebagai berikut
SYN (Synchronize)
CLIENT --> [SYN, seq=(num)] --> SERVER
SYN-ACK (Synchronize-Acknowledge)
CLIENT <-- [SYN, seq=0 (syn-seq), ACK, ack=seq-client+1] <-- SERVER
ACK (Acknowledge)
Untuk mengetahui Cycle TCP ini kita dapat menggunakan tools Wireshark dari Windows Client untuk melakukan Request ke Server 8080 milik Golang.
Dari sini terlihat proses Three-Way Handshake telah dilakukan oleh Client dan Server, untuk detail prosesnya saya akan tuliskan sebagai berikut ini
SYN Client
Segmen 1 (Paket 12) - SYN dari Klien ke Server
Seq=0, Ack=0, SYN
192.168.110.107 -> 192.168.110.100 [SYN, Seq=0]
SYN-ACK
Segmen 2 (Paket 13) - SYN-ACK dari Server ke Klien
Seq=0, Ack=1, SYN-ACK
192.168.110.100 -> 192.168.110.107 [SYN, Seq=0, Ack=1]
ACK
Segmen 3 (Paket 15) - ACK dari Klien ke Server
Seq=1, Ack=1, ACK
192.168.110.107 -> 192.168.110.100 [ACK, Seq=1, Ack=1]
Setelah proses Three-Way Handshake telah dilakukan maka HTTP/s Terminilogi akan dilakukan antara Client dan Server berupa Request dan Response, hal ini bisa dicontohkan seperti berikut ini
Request Raw
POST /login.php HTTP/1.1
Host: 192.168.110.100
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7
username=asd&password=asd
Client akan mengirimkan ada 3 part
Method (GET|POST|PUT etc)
Header (User-Agent, Host, etc)
Body (JSON / Post Body)
Response
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
{
"status": 200,
"msg": "ok"
}
Server akan mengirimkan 3 part juga
Seperti yang saya coba tuliskan, di atas adalah sebuah proses Cycle untuk Three-Way Handshake untuk settle koneksi antara Client dan Server untuk kasus protokol HTTP ataupun protokol lainnya. Dari pengetahuan di atas sebenarnya tidak begitu berpengaruh dalam bagaimana proses Logging yang dapat dilakukan melalui Raw TCP ini, di atas hanyalah intermezo saja hehe.
Jika saya sudah bertele-tele menjelaskan tentang Three-Way Handshake TCP, kali ini akan berada di sisi server menggunakan NGREP (Network Grep), jika tadi melakukan inspeksi HTTP Request dengan menggunakan Wireshark yang berada di sisi Client, maka NGREP akan digunakan pada sisi Server yang menghosting aplikasi web-nya.
sudo ngrep -d any -W byline ".*" port 8080
Kita dapat melakukan inspeksi secara Real Time semua Request & Response yang ada di dalam server tertuju ke serve terkhusus ke port 8080. Fungsi dari -W byline ".*"
agar lebih mempercantik output yang ada.
Raw Output
Splited Output
*PS: Di situ terlihat ada Cookies Khas PHP (PHPSESID) karena memang pada port 80 sedang listing riset dengan menggunakan PHP.
Dengan minimum CLI Command kita sudah mendapatkan sebuah Raw Request dan Response HTTP dari Client yang ada, tapi hasilnya masih sangat-sangat kotor sekali, cara terbaik untuk melakukan Remote Logging dari TCP Raw ini adalah dengan melakukan Pipingin lagi, kali ini saya menggunakan python3
untuk parsing.
import sys,re
request = ""
reqs = False
for data in sys.stdin:
regex_method = r"GET|POST|HEAD|PUT|DELETE|OPTIONS|TRACE|CONNECT"
if re.search(regex_method, data):
reqs = True
request = data
continue
if reqs:
if len(data) < 3 and "#" in data:
reqs = False
print("Request: "+request+"\n")
request = ""
request += data
sudo ngrep -d any -W byline ".*" port 8080 | python3 ppp.py
Inti dari script tersebut adalah dengan melakukan looping pada input STDIN via piping dari ngrep itu sendiri, setelahnya tiap-tiap Raw HTTP Request akan disimpan pada variable request
yang mana dapat diolah lagi atau langsung dikirimkan ke tempat-tempat Log saver yang diinginkan untuk mengolah-nya yang penting sekarang variable request
sudah berisi log “bersih” dari client.
Terima kasih telah membaca teman-teman, ngrep
ini adalah tools yang menemani saya dalam menjadi SOC L1-L3 pada karir saya sebelumnya, dan juga dalam kompetisi Attack & Defense sangat saya andalkan untuk mengintip payload-payload milik team lain yang sedang menyerang xixi. Mohon maaf jika ada kesalahan dalam penjelasan yang saya tuliskan saya sangat siap untuk merevisi dan berdiskusi! <3
Maybe you want to read: