Skip to content

Commit d7a10b0

Browse files
committed
- psc: reworking bounce-commands with regard to rate limiting and fixing a bug
where data is sent to the pty when it shouldn't. Note, that this changes the way -B parameters have to be constructed as explained in the README.
1 parent 3b56cce commit d7a10b0

File tree

8 files changed

+247
-102
lines changed

8 files changed

+247
-102
lines changed

README.md

Lines changed: 83 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ The session will be encrypted with `aes_256_ctr` of a PSK that you choose in the
117117
the packet size, where every byte counts since on interactive sessions and due to
118118
Base64 encoding, each typed character already causes much more data to be sent.
119119

120-
121120
UART sessions may be used via `screen` but for example not via `minicom` since
122121
minicom will create invisible windows with status lines and acts like a filter
123122
that destroys PSC's protocol. PSC tries to detect filtering and can live with
@@ -154,58 +153,80 @@ to an address different from `127.0.0.1`, so you can share the proxy in your loc
154153
Bounce commands
155154
---------------
156155

157-
*psc* contains features to allow TCP-connections or binary data blobs being forwarded from/to remote
156+
*psc* features allow TCP-connections or binary data blobs being forwarded from/to remote
158157
devices across multiple hops even if it is not possible to install the `pscr` binary at
159158
the remote site. This is very useful for forensic purposes if you do not have any means
160159
to otherwise download artefacts from the device (which can be an UART connected phone for example)
161160
or need to forward connections without touching the FS to not destroy evidence on the system
162161
or when the root-FS is ro mounted and you can't upload your tool-set.
163162

163+
This is a really cool feature, as you can see your TCP connection hop through your local tty
164+
to a remote box without the need to install anything remotely.
165+
164166
This solely works by local pty punkrock and handing over a bounce-command to `pscl` that it will
165167
drop on the remote shell (without `pscr` running) and some state engine magic that filters out
166168
and handles the data at the local side. Usually this requires to set the remote pty to raw mode
167-
at first before issuing the actual command.
169+
at first before issuing the actual command and some other details that are passed to `-B`. The
170+
argument is split into the following parts:
171+
172+
* The local port to trigger the command upon connect, followed by `:`, e.g. `1234:`.
173+
* The cmd that sets the remote tty to raw mode, usually `stty -echo raw` or
174+
`python -c "import tty;tty.setraw(0)"` (take care to get the quotes right, as `-B` also needs
175+
to be quoted) or anything similar.
176+
* A "GO" marker issued by remote that tells `pscl` to start sending data to avoid a race between
177+
`stty` actually happen and the start of the cmd, e.g. a `echo GO` is perfect.
178+
* The trigger command itself, e.g. `nc 127.0.0.1 22` to bounce local port 1234 to remote's SSH
179+
server
180+
* optionally a FIN marker issued by remote so you notice that trigger command has been finished
181+
i.e. you can kill your local connection to port 1234, which allows `pscl` to reset its tty state.
182+
`echo FIN` will do it. Recommended, as otherwise you can have trouble recognizing the end of
183+
your command.
184+
* All four previous commands are separated by `;` and enclosed in brackets.
185+
186+
Examples:
168187

169188
If you want to forward a TCP connection, this example requires `stty` and `nc` installed on the
170189
device, but it could theoretically be anything else that does equivalent.
171190

172191
Start a local session:
173192

174-
`TERM=dumb ./pscl -B '1234:[stty -echo raw;nc example.com 22]'`
193+
`./pscl -B '1234:[stty -echo raw;echo GO;nc example.com 22;echo FIN]'`
175194

176-
This will issue the command `stty -echo raw;nc example.com 22` to the remote device if you
177-
connect locally to port 1234 and then just forwards any data it sees back and forth and
178-
rate-limiting the traffic so it will not exceed the devices' tty speed (115200 is the default).
195+
This will issue the command `stty -echo raw;echo GO;nc example.com 22;echo FIN` to the remote
196+
device if you connect locally to port 1234 and then just forwards any data it sees back and forth
197+
and rate-limiting the traffic so it will not exceed the devices' tty speed (115200 is the default).
179198

180-
When the local session is started, connect to the remote device by UART, ssh or whatever it is and once
181-
you have the remote shell, also type locally:
199+
When the pscl session is started, connect to the remote device by UART, `ssh -e none ...` or
200+
whatever it is and once you have the remote shell, also type locally:
182201

183202
`ssh [email protected] -p 1234` to bounce the SSH connection from your local box across the remote
184-
device to the `example.com` destination. Of course the `pscr` variant is preferred as it is only possible
185-
to bounce a single connection at a time (although you can pass multiple `-B` commands for various
203+
device to the `example.com` destination. Of course the `pscr` variant is preferred as `-B` can only
204+
bounce a single connection at a time (although you can pass multiple `-B` commands for various
186205
forwards) and theres a chance to hang the shell after the TCP session since the pty is in `raw -echo`
187206
mode and depending on whether the final remote peer also closes the connection, it might be
188207
that the shell just hangs after that. If you happen to find a pscl notification that the connection
189208
has finished and see a prompt, you should `reset` it, so that a new connection can be started.
190-
While data is being forwarded, you will see 7bit ASCII `<` and `>` notifications in `pscl` which are just
191-
local for easier debugging and progress detection.
209+
While data is being forwarded, you will see 7bit ASCII `<` and `>` notifications in `pscl` which
210+
are just local for easier debugging and progress detection.
192211

193-
Note that the connection to the remote site has to be 8bit clean, i.e. the ssh, telnet, UART or whatever
194-
channel *must not handle escape sequences* (unlike when using `pscr`). For ssh connections this means you
195-
have to use `ssh -e none` in the `pscl` session.
212+
Note that the connection to the remote site has to be 8bit clean, i.e. the ssh, telnet, UART or
213+
whatever channel *must not handle escape sequences* (unlike when using `pscr`). For ssh connections
214+
this means you have to use `ssh -e none` in the `pscl` session.
196215

197216
Next, following some examples to handle binary file xfer where *rfile* denotes the remote file and
198217
*lfile* the local file.
199218

200219
To start a session to drop remote files, locally:
201220

202-
`TERM=dumb ./pscl -B '1234:[stty -echo raw;dd of=rfile.bin bs=1 count=7350;echo FIN]'`
221+
`./pscl -B '1234:[stty -echo raw;echo GO;dd of=rfile.bin bs=1 count=7350;echo FIN]'`
203222

204223
Where you need to specify the amount of data that the remote side is expecting. It would also
205224
work without (e.g. `cat>...`) but then the session will hang after transmission has finished as
206-
`cat` is endlessly expecting input.
225+
`cat` is endlessly expecting input. By using `dd count=...`, you will get a clean exit and be notified
226+
about it by the FIN marker.
207227

208-
Then, ssh or whatever is necessary to get a shell on the remote device. Again, locally:
228+
Then, ssh or whatever is necessary to get a shell on the remote device from within the just
229+
started `pscl` session. On a second terminal locally:
209230

210231
`dd if=lfile.bin|nc 127.0.0.1 1234`
211232

@@ -214,50 +235,67 @@ forwarding the binary data of the local `lfile.bin` to remotes `rfile.bin`. Due
214235
this can take a while and you *only trust your psc progress screen* whether the transfer is finished.
215236
The local `dd ...|nc ...` command will only show you the local status which can eat entire files
216237
in msecs due to local TCP buffers while the file is still being transfered through the pty.
217-
So make sure you only press `Ctrl-C` when the *psc* screen tells you it is finished.
238+
So make sure you only press `Ctrl-C` when the *pscl* screen tells you it is finished or you see
239+
the `FIN` end marker being echoed back to you on the `dd ...|nc ...` session.
218240

219241
Likewise, similar commands could be used to transfer binary data from a remote device to the
220242
local box for forensic purposes. Again, start of the session locally:
221243

222-
`TERM=dumb ./pscl -B '1234:[stty -echo raw;dd if=rfile.bin]'` or
244+
`./pscl -B '1234:[stty -echo raw;echo GO;dd if=rfile.bin]'` or
223245

224-
`TERM=dumb ./pscl -B '1234:[stty -echo raw;cat rfile.bin]'`
246+
`./pscl -B '1234:[stty -echo raw;echo GO;cat rfile.bin]'`
225247

226248
Then, ssh to remote device to get the shell, then again locally:
227249

228250
`nc 127.0.0.1 1234|dd of=lfile.bin bs=1 count=7350`
229251

230252
To obtain `rfile.bin` of size 7350 copied to local file `lfile.bin`
231253

232-
If `stty -echo raw` is not available on the device, something like `python -c 'import tty;tty.setraw(0)'`
233-
also works. Note that on the remote device you need to have a tty (not just a port-shell) when using bounce
234-
commands, since the `stty` command to set raw mode requires a real tty.
254+
If `stty -echo raw` is not available on the device, something like
255+
`python -c "import tty;tty.setraw(0)"` also works. Note that on the remote device you need to have
256+
a tty (not just a port-shell) when using bounce commands, since the `stty` command to set raw mode
257+
requires a real tty.
235258

236-
If doing all the tests only locally w/o any connection between `pscl` and the command that you bounce,
237-
you need to add `TERM=dumb` before the `pscl ...` comands. It can be omitted if running across a connection.
238259

260+
UART / modems / Flow Control
261+
----------------------------
239262

240-
Flow control
241-
------------
263+
If *psc* runs across a serial connection, lost bits can kill all your fun. If you run
264+
without HW FC you will eventually experience bitloss and hung connections, in particular
265+
as there is no throttling when the device is sending data in your direction when using
266+
*bounce commands*. Dumping data to the device works better as this data goes through
267+
the `pscl` rate limits.
242268

243-
If *psc* runs across a serial connection, it is very likely that flow control is disabled and sending data
244-
too fast would just make it vanish and corrupt the session. In this case you have to invoke *both ends* with
245-
the `-l` parameter to set a baud rate (e.g. `-l 115200`). If bounce commands are used the same problem exists
246-
with the pty in raw mode and rate limiting is automatically enabled to `115200` but a higher value
247-
can be set if desired.
248-
Note that browsing animated web sites is pure PITA with rate limiting from the 90's even though *psc*
249-
does its best to still allow typing the shell during the connection. SSH connections OTOH work surprisingly
250-
good.
269+
However, here are some tips that worked for me under circumstances when it is not
270+
possible to use `pscr` on the device and HW FC. This only applies when using UARTs, as this
271+
is a potentially unreliable transport channel.
251272

252-
UART / modem line
253-
-----------------
273+
* do not enable soft FC, as this would tamper the 8-bit channel
274+
* when possible use HW FC - or if not - you have to disable FC alltogether
275+
* Use `pscr` on the device so you can set rate limiting for data being sent into your direction.
276+
As the direction towards the device is always rate limited, you can use bounce commands to
277+
dump a cross-compiled `pscr` binary to the device and start a two-way rate limited session with it.
278+
* use high quality cables with proper shielding and UART chipsets with large buffers
279+
* apply the tio-limit patch from the contrib folder, as tio is buffering input bytes which could
280+
lead to writing-peeks that exceed the set rate
281+
* use `tio -o 1` or `-o 2` to add delays between sent output-bytes
282+
* use a conservative rate limitig (i.e. prefer `38400` although serial line has set `115200`)
283+
* compile `psc` with `-DRESPECT_UART_BUFSIZE=4096`, however this will make the session very slow
284+
285+
Inside the `contrib` folder you will also find a `tio-noprefix` patch to disable escape-character
286+
processing but this patch is only necessary for older versions, as upstream already accepted and
287+
integrated this patch. I really recommend using `tio` when using UARTs.
288+
289+
290+
When using bounce commands across *tio*, you have to add to your `~/.tioconfig` file:
291+
292+
```
293+
[default]
294+
295+
prefix-ctrl-key = none
296+
```
254297

255-
Some UART chipsets seem to have different RX vs TX speeds. So even though you have a 115200 baud
256-
UART connection set up, the local (upload) part is even more limited or otherwise data bits are lost.
257-
During my raspi tests, I had to use `pscl -l 57600 ...` on the local side while using `pscr -l 115200 ...`
258-
on the device. For bounce-commands I also had to use the 57600 even if the connection was at 115200.
259-
If possible also use soft flow control with your serial console program. Also check the `contrib`
260-
folder in order to patch your console program to be escape-character safe.
298+
which disables ESC-handling and gives you an 8-bit clean channel.
261299

262300

263301
SIGUSR1 / SIGUSR2

contrib/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,23 @@ in order to allow serial console session w/o tapering of the I/O stream so you
1414
can pipe arbitrary data through your pty for the bounce command (this patch
1515
works like ssh's `-e none`).
1616

17+
Adding this patch to current versions is not necessary anymore as its already
18+
been integrated.
19+
1720
Then add to your `~/.tioconfig`:
1821
```
1922
prefix-ctrl-key = none
2023
```
2124

2225
Which disables prefix parsing.
2326

27+
28+
29+
tio limit patch
30+
---------------
31+
32+
tio will collect input bytes into a buffer before sending it to the serial line,
33+
which could lead to peeks above the acceptable baudrate. This dirty patch adds rate
34+
limiting in the write cycle. Also consider using `tio -o 1` if you get bit flips
35+
during transmission.
36+
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
diff --git a/src/tty.c b/src/tty.c
2+
index 74f6ab3..9392552 100644
3+
--- a/src/tty.c
4+
+++ b/src/tty.c
5+
@@ -27,6 +27,7 @@
6+
#include <stdarg.h>
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
+#include <stdint.h>
10+
#include <sys/time.h>
11+
#include <unistd.h>
12+
#include <string.h>
13+
@@ -224,16 +225,27 @@ inline static unsigned char char_to_nibble(char c)
14+
void tty_sync(int fd)
15+
{
16+
ssize_t count;
17+
+ struct timeval tv;
18+
+ static struct timeval last_tv = {0, 0};
19+
+ uint32_t byte_rate = option.baudrate/10, idx = 0;
20+
21+
while (tty_buffer_count > 0)
22+
{
23+
- count = write(fd, tty_buffer, tty_buffer_count);
24+
+ gettimeofday(&tv, NULL);
25+
+ uint64_t tdiff_usec = tv.tv_sec*1000000 + tv.tv_usec - (last_tv.tv_sec*1000000 + last_tv.tv_usec);
26+
+ if (tdiff_usec < (1000000*1.0/byte_rate))
27+
+ continue;
28+
+ last_tv.tv_sec = tv.tv_sec;
29+
+ last_tv.tv_usec = tv.tv_usec;
30+
+
31+
+ count = write(fd, tty_buffer + idx, 1);
32+
if (count < 0)
33+
{
34+
// Error
35+
tio_debug_printf("Write error while flushing tty buffer (%s)", strerror(errno));
36+
break;
37+
}
38+
+ idx += count;
39+
tty_buffer_count -= count;
40+
fsync(fd);
41+
tcdrain(fd);

src/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ POSIX=-D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600
1111

1212
CXX=c++
1313
DEFS=-DPSC_READ_KEY=$(KEY1) -DPSC_WRITE_KEY=$(KEY2) -DSTART_BANNER=$(BANNER)
14+
#DEFS+=-DRESPECT_UART_BUFSIZE=4096
1415
CXXFLAGS=-c -Wall -O2 -std=c++11 -pedantic
1516

1617
.PHONY: all clean

0 commit comments

Comments
 (0)