Skip to content

[Overflow Bug]RTKLIB stream.c: overflow in FTP path parsing via oversized password #788

Description

@neosys007
I would like to report another current-head `Intra-Object Overflow` in `src/stream.c`, this time in the FTP/HTTP parser used by `openftp()`.
This is not the earlier `stropen(stream->path, path)` issue, and it is also distinct from the already narrower `user -> passwd` parser overflow. The problem here is the password component of the documented FTP path grammar.

The relevant current-head object layout is:

```c
typedef struct {
    int state;
    int proto;
    int error;
    char addr[1024];
    char file[1024];
    char user[256];
    char passwd[256];
    char local[1024];
    int topts[4];
    gtime_t tnext;
    thread_t thread;
} ftp_t;
```

The helper used by `openftp()` contains:

```c
static void decodeftppath(const char *path, char *addr, char *file, char *user,
                          char *passwd, int *topts)
{
    char buff[MAXSTRPATH], *p, *q;
    ...
    strcpy(buff, path);
    ...
    if ((p = strrchr(buff, '@'))) {
        *p++ = '\0';
        if ((q = strchr(buff, ':'))) {
            *q = '\0';
            if (passwd) strcpy(passwd, q + 1);
        }
        *q = '\0';
        if (user) strcpy(user, buff);
    }
    ...
}
```

And `openftp()` passes the embedded fields directly:

```c
ftp->local[0] = '\0';
...
decodeftppath(path, ftp->addr, ftp->file, ftp->user, ftp->passwd, ftp->topts);
```

Why I believe this is a real current-head bug:

1. The documented grammar already allows:

```text
STR_FTP [user[:passwd]]@address/file_path[::T=...]
```

2. A caller can keep the total path below `MAXSTRPATH == 1024`, so this report does not depend on the earlier top-level `stream->path` overflow.

3. Even with total path length below `1024`, the parsed password component can still exceed `255`.

4. There is no current-head component-length validation before:

```c
if (passwd) strcpy(passwd, q + 1);
```

5. In `ftp_t`, `passwd[256]` is immediately followed by the live field `local[1024]`.

So an oversized password causes a direct intra-object overwrite from `passwd` into `local`.

A minimal trigger shape is:

```text
u:EEEE...(320 bytes)@host/file
```

This has total length only `332`, so it stays below `MAXSTRPATH`, while the password component itself is `320` bytes.

I also built a reduced source-faithful proof that preserves the exact `ftp_t` member order and the exact `decodeftppath()` logic. Its output is:

```text
path_len=332
distance_passwd_to_local=256
passwd_component_len=320
overflow_bytes_into_local=65
local_prefix_hex=4545454545454545
topts_unchanged=1
```

That result shows:

- the total path remains only `332`
- `passwd[256]` is directly followed by `local[1024]`
- a `320`-byte password writes `65` bytes into the adjacent `local` field
- the overwrite stays localized and does not depend on a larger surrounding object overflow

I am making a narrow claim:

- this is current-head
- it is reachable from the documented FTP path grammar
- it is a real write-side intra-object overflow
- the concrete overwrite is `ftp->passwd[256] -> ftp->local[1024]`

I think the right fix is to validate parsed component lengths before copying and replace raw `strcpy()` with bounded copies for `user`, `passwd`, `file`, and `addr`.

Best regards
Pengpeng Hou
ISCAS

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions