Skip to content

Commit b650e82

Browse files
committed
Add steganography article
Add example notebook for steganography Simplify steganography notebook Reword steganography article Don't lint the documentation directory Remove pycodestyle lint warnings for documentation Add steganography demonstration
1 parent ce69e12 commit b650e82

3 files changed

Lines changed: 163 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Protocol Buffer Steganography
2+
3+
I started this project with the sole purpose of hiding some messages for a friend with some novel steganographic techniques, but I finally got carried away and chose to repurpose it for more general use cases, turning it into a full-fledged recording converter.
4+
5+
Technically, the idea behind protocol buffer steganography is very simple: the protocol buffer specification is flexible enough to accept unknown fields, and some servers don't remove those fields when storing uploaded messages, effectively allowing users to silently upload arbitrary data embedded into protocol buffer messages without introducing parse errors.
6+
7+
> [[...] messages created by your new code can be parsed by your old code: old binaries simply ignore the new field when parsing [...]](https://developers.google.com/protocol-buffers/docs/proto3#updating)
8+
> ***
9+
> [Unknown fields are well-formed protocol buffer serialized data representing fields that the parser does not recognize. For example, when an old binary parses data sent by a new binary with new fields, those new fields become unknown fields in the old binary.](https://developers.google.com/protocol-buffers/docs/proto3#unknowns)
10+
11+
Users that are in the know can build an extended descriptor in order to retrieve the concealed information stored on the extraneous fields.
12+
13+
## Practical example
14+
15+
1. Alice has a server which accepts anonymous uploads of protocol buffer messages with the following descriptor:
16+
```proto
17+
syntax = "proto2";
18+
19+
message InconspicuousMessage {
20+
required string greeting = 1;
21+
}
22+
```
23+
**Expected message example**
24+
```
25+
00000000: 0a0d 4865 6c6c 6f2c 2077 6f72 6c64 21 ..Hello, world!
26+
```
27+
28+
2. Bob uploads a crafted message, which includes a valid greeting along with a hidden string, by using the following descriptor:
29+
```proto
30+
syntax = "proto2";
31+
32+
message InconspicuousMessage {
33+
required string greeting = 1;
34+
required string hidden = 2;
35+
}
36+
```
37+
**Crafted message example**
38+
```
39+
00000000: 0a0d 4865 6c6c 6f2c 2077 6f72 6c64 2112 ..Hello, world!.
40+
00000010: 134a 6f68 616e 6e65 7320 5472 6974 6865 .Johannes Trithe
41+
00000020: 6d69 7573 mius
42+
```
43+
44+
3. Alice stores the uploaded message as-is on her public server because she's able to parse it without noticing any difference.
45+
4. Charles downloads the message and parses it with the extended descriptor in order to recover the hidden text.
46+
47+
## Demonstration
48+
49+
```console
50+
$ poetry run blobopera recording download https://g.co/arts/4NXntRPm8ut1ViL2A file.zip
51+
$ unzip file.zip
52+
Archive: file.zip
53+
extracting: message.txt
54+
$ cat message.txt
55+
Johannes Trithemius
56+
```
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
{
2+
"nbformat": 4,
3+
"nbformat_minor": 0,
4+
"cells": [
5+
{
6+
"cell_type": "code",
7+
"source": [
8+
"%%bash\n",
9+
"cat > alice.proto <<-END\n",
10+
" syntax = \"proto2\";\n",
11+
"\n",
12+
" message InconspicuousMessage {\n",
13+
" required string greeting = 1;\n",
14+
" }\n",
15+
"END"
16+
],
17+
"execution_count": 1,
18+
"outputs": []
19+
},
20+
{
21+
"cell_type": "code",
22+
"source": [
23+
"%%bash\n",
24+
"cat > bob.proto <<-END\n",
25+
" syntax = \"proto2\";\n",
26+
"\n",
27+
" message InconspicuousMessage {\n",
28+
" required string greeting = 1;\n",
29+
" required string hidden = 2;\n",
30+
" }\n",
31+
"END"
32+
],
33+
"execution_count": 2,
34+
"outputs": []
35+
},
36+
{
37+
"cell_type": "code",
38+
"source": [
39+
"%%bash\n",
40+
"protoc --encode InconspicuousMessage bob.proto > message.bin <<-END\n",
41+
" greeting: \"Hello, world!\"\n",
42+
" hidden: \"Johannes Trithemius\"\n",
43+
"END"
44+
],
45+
"execution_count": 3,
46+
"outputs": []
47+
},
48+
{
49+
"cell_type": "code",
50+
"source": [
51+
"%%bash\n",
52+
"protoc --decode InconspicuousMessage alice.proto < message.bin"
53+
],
54+
"execution_count": 4,
55+
"outputs": [
56+
{
57+
"output_type": "stream",
58+
"text": [
59+
"greeting: \"Hello, world!\"\n",
60+
"2: \"Johannes Trithemius\"\n"
61+
],
62+
"name": "stdout"
63+
}
64+
]
65+
},
66+
{
67+
"cell_type": "code",
68+
"source": [
69+
"%%bash\n",
70+
"protoc --decode InconspicuousMessage bob.proto < message.bin"
71+
],
72+
"execution_count": 5,
73+
"outputs": [
74+
{
75+
"output_type": "stream",
76+
"text": [
77+
"greeting: \"Hello, world!\"\n",
78+
"hidden: \"Johannes Trithemius\"\n"
79+
],
80+
"name": "stdout"
81+
}
82+
]
83+
},
84+
{
85+
"cell_type": "code",
86+
"source": [
87+
"%%bash\n",
88+
"protoc --decode_raw < message.bin"
89+
],
90+
"execution_count": 6,
91+
"outputs": [
92+
{
93+
"output_type": "stream",
94+
"text": [
95+
"1: \"Hello, world!\"\n",
96+
"2: \"Johannes Trithemius\"\n"
97+
],
98+
"name": "stdout"
99+
}
100+
]
101+
}
102+
]
103+
}

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ pycodestyle = ["+*", "-E741", "-E203"]
8181
pyflakes = ["+*"]
8282
pylint = ["+*"]
8383

84+
[tool.flakehell.exceptions."documentation/**"]
85+
pycodestyle = ["-*"]
86+
"*" = ["-*"]
87+
8488
[tool.flakehell.exceptions."tests/*.py"]
8589
flake8-bandit = ["-S101"]
8690
flake8-builtins = ["-A001"]

0 commit comments

Comments
 (0)