# Unauthenticated Remote-Control HTTP API in SmsForwarder (Insecure Default Security Mode)

SmsForwarder's optional HTTP server exposes remote SMS, call-log, , location, Wake-on-LAN, and configuration/clone operations. When the server is enabled, the default security mode is `0` (no signing key → signature verification disabled), while the sensitive API toggles are enabled by default. The request pipeline has no authentication fallback for mode `0`, and the server is not bound to loopback. An unauthenticated client that can reach the configured port can invoke the sensitive endpoints with no credential or valid signature.
var serverSignKey: String by SharedPreference(SP_SERVER_SIGN_KEY, "")
var safetyMeasures: Int by SharedPreference(SP_SERVER_SAFETY_MEASURES, if (TextUtils.isEmpty(serverSignKey)) 0 else 1)
Source: `app/src/main/kotlin/cn/ppps/forwarder/utils/HttpServerUtils.kt#L35-L39`
With no signing key configured (the default), `safetyMeasures` is `0`. The sensitive API toggles (clone, SMS, call, , battery, WOL) are enabled by default (`HttpServerUtils.kt#L74-L96`).
### 2. Signature check only runs in mode 1
if (HttpServerUtils.safetyMeasures == 1) {
HttpServerUtils.checkSign(t as BaseRequest )
Source: `app/src/main/kotlin/cn/ppps/forwarder/server/component/AppMessageConverter.kt#L93-L96`
In mode `0`, `checkSign()` is never called. `LoggerInterceptor` (`LoggerInterceptor.kt#L35-L48`) only checks whether an API was toggled off; it performs no authentication.
`HttpServerService` builds the server with `.port(HttpServerUtils.serverPort)` and starts it; no `inetAddress`/loopback restriction and no authentication handler are configured, so the server listens on the device's interfaces (`HttpServerService.kt#L18-L34`). The source default port is `5000` (`Constants.kt#L209`).
### 4. Controllers act after only JSON deserialization
`/sms/send`, `/sms/query`, `/call/query`, `/ /query`, `/ /add`, `/clone/pull`, `/clone/push`, `/location/query`, `/wol/send`, `/battery/query` perform their sensitive action after deserialization and the optional converter check only.
participant D as SmsForwarder device (server enabled, mode 0)
A->>D: POST /config/query {"data":{},"timestamp":0,"sign":""}
Note over D: safetyMeasures==0 -> checkSign() skipped
A->>D: POST /sms/query / / /query / /call/query
D-->>A: private SMS / contacts / call history
Use only a disposable emulator/device and mocked data. Do not send a real SMS or touch real user data.
1. Build/install the app at the tested commit.
2. Enable the integrated HTTP server; leave **Safety measures** at the default **None** and the API toggles enabled.
3. From another device on the same test network, send an unauthenticated request:
Observed (per source): the configuration response is returned instead of an authentication error.
4. The same unauthenticated shape reaches `/sms/query`, `/ /query`, `/call/query`, `/location/query`, `/clone/pull`, etc. Avoid `/sms/send` against a real number; demonstrate integrity with `/ /add` against a disposable DB.
Static source review at commit `a3d23026f0058420869163c1d5dfb463ce52fc15`. The default `safetyMeasures=0`, the mode-gated `checkSign()`, the auth-free `LoggerInterceptor`, and the lack of a loopback bind were confirmed in source. No binary was executed and no real device data was used; this is a static finding.
- **Confidentiality (High):** read private SMS, call history, contacts, and cached location (subject to the app's granted Android permissions).
- **Integrity (High):** send SMS from the device, add contacts, and export/overwrite the app's forwarding/configuration data via the clone API; send arbitrary Wake-on-LAN packets.
- Precondition: the user enables the HTTP server (its intended function) and leaves the default security mode. No Android account, app session, API key, or valid signature is required in mode `0`.
- Local-network reach is the direct scenario; internet-wide exploitation additionally requires the port to be exposed/forwarded.
- **Immediate:** bind the server to loopback by default, or require an explicit user action to expose it off-device.
- **Short-term:** make authentication mandatory whenever the server is network-reachable; generate and enable a secure random key by default; enforce it in a single interceptor before controller dispatch (including `/config/query`).
- **Defense-in-depth:** default sensitive API toggles to disabled; return a real `401/403` for unauthenticated requests instead of processing them.
Treat the HTTP control server as a security boundary: mandatory authentication before dispatch, loopback-by-default binding, and sensitive endpoints disabled until explicitly enabled.
- Project: [pppscn/SmsForwarder](
- Source reviewed at commit [`a3d2302`]( `HttpServerUtils.kt`, `AppMessageConverter.kt`, `LoggerInterceptor.kt`, `HttpServerService.kt`, `server/controller/*`
- Weaknesses: [CWE-306 Missing Authentication for Critical Function]( [CWE-862 Missing Authorization](
- Suggested CVSS 3.1: `9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N)`
Reported by hieuPenguinnn ([github.com/hieuPenguinnn](
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
