Tôi biết khi viết bài này sẽ có kha khá người ko vui,
Ông bà ta thường có câu “quá tam ba bận” mà, đôi khi nhai lại mãi 1 chủ đề nó cũng ko vui cho lắm …
Chẳng vậy mà dạo gần đây khi tôi đăng bài kỹ thuật ko còn thấy mấy ai quan tâm nữa.
Dạo này thì đầu óc cũng hơi bị đãng trí, ko còn nhớ được lâu nữa …
Loay hoay cả buổi sáng mới setup được lại cái môi trường để debug :<.
Thành ra post này cũng sẽ có cả những part lê thê liên quan đến vụ setup môi trường, đề phòng một ngày nào đó bị mất trí nhớ …
.
.
.
Thôi đi vào câu chuyện chính nào,
Vừa qua mình có cùng ae bên cơ quan tổ chức 1 vụ diễn tập về ATTT cho toàn thể ae làm sec trong đơn vị X nhà mình,
Mình có nhận chân ra đề, 4/8 bài,
Khi triển khai thì chỉ có duy nhất 1 bài được solve bởi mỗi 1 đội, khá là thất vọng vì những chall mình ra ko có nhiều ng làm đc , thất bại …
Sau đó mình có làm cái bài tập môn trên trường, đề tài về phân tích log.
Lúc làm demo thì lười quá, nên vác thẳng server diễn tập vẫn online ra làm demo.
Bật acunetix lên scan và vô tình thấy …
Sh*t …
)
Mình đã quên mất entrypoint này trong những lần phân tích trước!
Hầu như là bởi vì mình đều gửi request test đến “/api/spring/” mà quên mất ko đọc kỹ code về cách hoạt động của cái entrypoint này!
Và có lẽ là cũng khá nhiều người đã bị lãng quên cái entrypoint này giống mình,
Thử quét 1 vòng các target lớn mà trước kia fail, rất nhiều trường hợp bị mis-config, “quên” không block cái entrypoint này!
Demo nhỏ:
Về cái cách exploit thì cũng đơn giản thôi: tìm entrypoint => send serialized => RCE.
Phần quan trọng ở đây đó là cái entrypoint,
Trước đây mình bị nhầm tưởng entrypoint “/api/spring” này cũng hoạt động giống “/api/liferay”, nhưng thực ra là không phải!
_Env setup
Để bắt đầu, mình sẽ nói qua chút về bước setup 1 môi trường liferay portlet để có thể debug cái server này!
Nếu bạn bỏ qua phần này thì có thể Ctrl + F “The forgotten Entrypoint” và nhảy tới phần kỹ thuật!
Download và cài những thứ sau:
- Liferay Workspace: https://sourceforge.net/projects/lportal/files/Liferay%20Workspace/1.5.0.1/LiferayWorkspace-1.5.0.1-windows-installer.exe/download
- Liferay IDE (lưu ý chọn ver cũ thôi thì mới có tích hợp cả liferay cũ, ver mới chỉ có liferay <7.0): https://sourceforge.net/projects/lportal/files/Liferay%20IDE/2.2.4%20GA5/liferay-ide-eclipse-windows-x64-2.2.4-ga5-201507230603.zip/download (chọn workspace mà bên trên vừa cài xong)
- Download Server https://sourceforge.net/projects/lportal/files/Liferay%20Portal/6.2.5%20GA6/liferay-plugins-sdk-6.2-ce-ga6-20171101150212422.zip/download và Plugin SDK: https://sourceforge.net/projects/lportal/files/Liferay%20Portal/6.2.5%20GA6/liferay-plugins-sdk-6.2-ce-ga6-20171101150212422.zip/download giải nén ra đâu đó
- Nhớ cài maven trước
Tiếp theo mở cái Eclipse Liferay vừa cài ra, mở Window > Preferences > Liferay > Installed Plugin SDK và Add cái plugin vừa extract như này:
Sau khi add xong sẽ được như này:
Tiếp theo, chọn Window > Open Perspective > Liferay để chuyển sang perspective của liferay cho nó chuyên nghiệp ))
Chọn Window > Preferences > Servers > Runtime Environments và Add server liferay tomcat vừa extract vào đây,
Chọn File > New > Liferay Plugin Project ,
Phần Build type chọn Maven:
Phần Active profiles, bấm vào cái dấu cộng ở bên cạnh như này:
Chọn liferay 6.2 và click ok:
Then, Finish và chờ nó load 1 lúc:
Sẽ được kq như vậy
Mở file pom.xml, thêm dòng này vào: (Đôi khi maven ko auto build thì b phải tự tay update project nhé)
Như vậy để có thể trace code dễ dàng hơn đối với 1 số lib của liferay (do quá trình trace code, eclipse ko tìm được reference nằm bên trong web server or gì đó mình cũng kb nữa )) ).
Ví dụ như debug cái entrypoint “/api/liferay” như những bài viết trước, mình tìm đến class handler chức năng đó nằm trong file jar nào đó và đặt breakpoint:
Và như vậy đã có thể debug ngon lành:
- Lưu ý khi debug trực tiếp sẽ không thể đọc được variable do IDE ko map được các variable, do đó phải link source cho library kỹ càng nhé!
_The forgotten Entrypoint
Đi vào chủ đề chính nào,
Lưu ý cực mạnh: Để debug suôn sẻ, hãy link source cho tất cả các lib khi debug, kể cả apache tomcat nhé
Để ý lại cái entrypoint mà Acunetix thần thánh đưa ra như sau:
“/api/spring/com_liferay_portal_service_spring_UserService-http”
“com_liferay_portal_service_spring_UserService” nhìn khá giống một class trong liferay servlet:
Theo phán đoán ban đầu của mình, entrypoint này hoạt động theo kiểu dynamic load một cái object nào đó dựa theo cái name ở đằng sau path kia để handle (mãi sau mới biết nó được gọi là DispatchServlet :< ).
Và theo phán đoán tiếp theo của mình thì cái class UserService kia là thủ phạm gây ra vụ deserialize này …
Nhưng mình đã nhầm sau khi trace code lòng vòng mất nguyên buổi sáng …
Sau đó mình quay trở lại từ ban đầu, bắt đầu từ class handle cái entrypoint “/api/spring” đó là: com.liferay.portal.spring.servlet.RemotingServlet
(Quên đề cập! Để tìm ra cái class handle chức năng đó thì tìm trong web.xml theo entrypoint để trace ra nhé!)
Và đây là đoạn code xử lý:
Set breakpoint ở line 66 và send request tới!
super.service() gọi method service() của DispatchServlet (do RemotingServlet thừa kế, feeling hơi tiếc nuối vì ngày xưa ko chịu học OOP ra hồn :< ).
Tiếp tục F5, F6 cho tới khi vào trong method doDispatch() của DispatchServlet
Line 753: Thực hiện lấy handler object từ request url
Chính xác hơn đoạn xử lý nằm trong AbstractUrlHandlerMapping.getHandlerInternal()
Backtrace có dạng như này
Quay trở lại DispatchServlet, sau khi đi qua line 790, object được deserialize
ha ở đây là object của HandlerAdapter, jump tiếp tục vào ha.handle().
ha.handle() thực hiện gọi method handleRequest() của mappedHandler phía trên
handler lúc này chính là object của org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
Jump tiếp tục vào handler.handleRequest(), lúc này sẽ lead tới method handleRequest của HttpInvokerServiceExporter.
Tại đây, line 72, object được deserialize
Tiếp tục F5, jump vào readRemoteInvocation()
Ở đây request.getInputStream() chính là post data lấy từ request.
Tiếp tục jump vào doReadRemoteInvocation()
Và ở đây chính là nơi xảy ra deserialization
Tóm gọn lại, flow để deserialize object truyền vào như sau:
Tìm lại trong file config remoting-servlet.xml, ta có thể thấy rất nhiều entrypoint khác cũng sử dụng class org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter để handle request:
Well, như vậy cũng có lẽ là đã xong,
Về cơ bản thì mình thấy root cause của entrypoint này thú vị hơn “/api/liferay” rất nhiều )).
Khá là mất time để phân tích, nhưng cũng học được thêm khá nhiều thứ.
- Lưu ý một lần nữa cho những ai muốn debug: nhớ link source cho tất cả các lib mà có debug tới!
Hy vọng đây là post cuối cùng về cái bug liferay này ))
Vài lời trước khi end bài:
Nhiều bạn có thắc mắc tại sao mình ko viết bài “in English”.
thì cái một phần là do mình dốt tiếng anh, phần nữa là do lòng tự tôn tiếng mẹ đẻ hơi cao một chút nên kệ ), bác Tây nào cần đọc thì đi mà translate, me dont have time!
__Jang__
2.180 lượt xem