Skip to content
H1ue Ahi Me

H1ue Ahi Me

hackmd.io • October 15, 2026

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

![status](

![CVSS](

![CWE](

![component](

**Reported by:** hieuPenguinnn ([github.com/hieuPenguinnn](

**Project:** [pppscn/SmsForwarder]( Android SMS forwarding app with an integrated HTTP control server

**Affected:** verified at commit `a3d23026f0058420869163c1d5dfb463ce52fc15` (app version `3.5.0`, versionCode `55`, targetSdk `33`)

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](