Khi được tiếp xúc với các hệ thống code bằng Java, mình thấy có khá nhiều website sử dụng thư viện JasperReports trong việc render báo cáo. Tuy là một công cụ mạnh mẽ nhưng Japser sở hữu function mà có thể lợi dụng để RCE. Nên mình quyết định mày mò nó một chút, cũng như chia sẻ những kiến thức mà mình biết và tìm hiểu được được về Jasper.
Code Injection In Jasper
Một chút khái niệm
Jasper là một thư viện có mục đích chính dùng để render nội dung. Được sử dụng chủ yếu cho việc chuyển đổi từ file có cấu trúc sang văn bản báo cáo, có thể xuất ra với nhiều định dạng khác nhau như: PDF, HTML, Excel,…
Jasper hỗ trợ nhiều file đầu vào:
- JRXML: file XML với các attribute của jasper
- JASPER: file được compile từ jrxml
- CSV, JSON, XML,…
Trong phạm vi bài viết, mình sẽ đề cập đến chức năng phổ biến khi sử dụng thư viện JasperReports mà mình hay gặp nhất, đó là chuyển đổi file JRXML sang định dạng PDF.
Cấu trúc file JRXML
Như mình đã nói ở trên, file JRXML cơ bản là một file XML nhưng các attribute sẽ tuân theo tiêu chuẩn của một Jasper Template, nên ta cũng sẽ có tag như bao file XML khác.
, trong thẻ đó ta sẽ khai báo những thuộc tính cơ bản của report như in dọc hay ngang, ngôn ngữ sử dụng, tên báo cáo, đồng thời có thêm một vài XSD references. Nhưng về cơ bản thì ta chỉ cần thuộc tính name là đủ, các thuộc tính còn lại là tùy chọn, có thể có cũng có thể không.Bên trong thẻ root, ta có thể chèn vào các thẻ khác nhau để biểu đạt nội dung của báo cáo, từ các label hoặc chữ cố định đến các biến có giá trị thay đổi, cũng như có thể chèn vào hình ảnh, format bảng, chức năng tính toán,…Một file JRXML in ra chữ Hello World đại khái sẽ như này:
Chi tiết về các attribute sử dụng trong JasperReport các bạn có thể tham khảo tại Jasper document . Thay vào đó, mình sẽ đi vào tính năng khiến cho Jasper trở thành công cụ render report mạnh mẽ, đó là Expressions.
Expressions
Kiến thức nền tảng
Là một tính năng mạnh mẽ Jasper cung cấp cho người dùng, các expression (có thể dịch ra là các biểu thức, các mô tả) được sử dụng để khai báo biến trong báo cáo, cũng như hỗ trợ xử lý các biến đó một cách linh hoạt từ hiển thị cho đến tính toán. Để làm được điều đó, các expression sẽ được viết bằng ngôn ngữ lập trình, mà mặc định trong Jasper sẽ là Java code.
Vừa là tính năng có thể giúp việc xử lý dữ liệu in ra trên báo cáo được trực quan và dễ dàng, nhưng cũng vừa là nơi để chèn code tùy ý. Tuy nhiên có một lưu ý rằng code truyền vào bên trong các expression cần phải trả về một kiểu dữ liệu nhất định để hoạt động, nếu không sẽ gây lỗi khi render.
Các expression này sẽ được trigger trong quá trình render file JRXML sang PDF. Nếu không có cơ chế phòng thủ phù hợp, mình hoàn toàn có thể khai thác lỗi code injection để RCE, đọc tải file,…
Ví dụ
Để ví dụ thêm trực quan, mình có một đoạn code spring đơn giản, phục vụ việc upload 1 file JRXML và trả về file report.pdf
Mình sử dụng Jasper phiên bản 6.21.4 là ver mới nhất của dòng 6., vì mình thấy dòng 6. hiện đang được sử dụng nhiều nhất.

Sử dụng luôn ví dụ bên trên, thay vì sử dụng thẻ để hiển thị nội dung tĩnh trong field , mình sẽ sử dụng thẻ dùng để diễn đạt nội dung bên trong thẻ , đại khái lúc này file JRXML sẽ như sau:
Tải file PDF về và output câu lệnh sẽ hiển thị ra ngoài:
Ngoài cách tấn công này ra thì một số bài viết trên mạng thì đa số họ sẽ tấn công vào thẻ trong quá trình khai báo biến , nên mình sẽ không nhắc đến nó nữa, mình sẽ để chúng ở trong phần ref để các bạn có thể tham khảo thêm.
Bypasses
Giả sử như dev giờ đã biết được tầm nghiêm trọng của vấn đề và có một số biện pháp phòng thủ, mình sẽ đưa ra một số phương pháp có thể bypass cơ chế phòng thủ như sau:
Expression Attribute
Hướng đầu tiên mà mình nghĩ đến trong các trick bypass đó là sử dụng các thẻ khác ngoài những thẻ đã có sẵn payload trên mạng. JasperReports có kha khá các expression khác mà mình có thể lợi dụng, vì bản thân trường dữ liệu nào cũng cần có nhu cầu hiển thị và xử lý dữ liệu linh hoạt.
Ta có thể sử dụng attribute defaultValueExpression bên trong thẻ parameter, rồi call tại thẻ textField quen thuộc. Để call đến param, trong textFieldExpression mình sẽ sử dụng cú pháp $P{Tên param}:
Tại thẻ variable thì còn một expression nữa ngoài variableExpression là initialValueExpression cũng có thể chèn code, call đến variable thông qua syntax $V{Tên variable}:
title nếu sử dụng detail sẽ luôn hiển thị giá trị null

Mò mẫm document, mình thấy nếu như không nhất thiết phải lấy output của câu lệnh thì tồn tại một số Expression đặc thù có thể lợi dụng để RCE, nhưng nó sẽ gây ra lỗi. Attribute imageExpression thuộc thẻ vốn được sử dụng để Jasper có thể lấy ảnh từ một URL xác định dạng String, khi không đáp ứng đúng dữ liệu yêu cầu, câu lệnh sẽ trả về lỗi nên mình vẫn có thể lợi dụng để thực hiện các câu lệnh blind để confirm.
Tại đây vì mình truyền vào Runtime.getRuntime().exec(), output trả về sẽ là một object thuộc ProcessImpl nên chương trình sẽ văng ra lỗi như sau:
Nếu như set up để câu lệnh trả về dữ liệu, Jasper khi cố gắng truy cập URL để lấy byte ảnh về thất bại cũng sẽ văng ra lỗi, tuy nhiên mình thấy extract thông tin 1 dòng thì có vẻ không giòn lắm:

Tiếp đến là attribute printWhenExpression có thể được khai báo trong thẻ hoặc , sử dụng khi ta chỉ muốn dữ liệu được in ra theo một điều kiện nhất định, nên nó yêu cầu cần trả về giá trị kiểu Boolean hoặc giá trị null. Việc truyền java code vào attribute này cũng sẽ gây lỗi lúc render PDF nên ta ưu tiên bằng một số câu lệnh blind như curl, ping,…

Attribute conditionExpression thuộc thẻ
