-
Notifications
You must be signed in to change notification settings - Fork 1
Write ups (Eng)
Thinkerer
Hologram
Eloquent
Nodbrary
Ribbons
● C++, grpc, protobuf
● Internal file-based storage
There were two logic vulnerabilities in the service.
Thinkirer is a simple messanger service. Users can write messages to each others. Thinkerer has grpc-based protocol. See list of aviable methods in the thinkerer.proto file.
The service has ability to forward one message to another user. To do this you should add MsgForward msg_forward = 6;
field to a message and specify a forwarded message id and timestamp.
Look at the GetMessageById function to how the service finds the forwarded message. You can see there, there is no message permission checks: you can forward any message you like, you should only know its id and timestamp.
Message id is the number thats incremented by one with each message. So, to get a valid message ids, you can send a message to yourself and see its id (and decrement it).
To get a valid message timestamp you should first notice that you should find timestamp approximately. So if need recent flags you can use current time as message timestamp.
To patch this vulnerability you should add permission to check the GetMessageById function.
- C#, http-server, websockets, just binaries in vulnerable image
- 3 dimensional storage of "holograms"
- 2 logic vulns in service
"Holograms" is simple documents storage service, with a "limited" view in three-dimensional space.
You could open a websocket connection with negative radius, which provides you to bypass limits of "view" WsServer.cs#L178
ws://10.60.27.1:5000/ws/holograms?x=0&y=0&z=0&rad=-100000000
Query() methods provided query string parsing into the List<(string key, string value)>, so it can store same (key - value) pairs.
So, WsServer.cs#L142 check was a bit incomplete, so you could send same keys twice, but only first will be checked correctly. So, only the last kv pair, which is met in kv List, will be used after using custom ToDictionary() method.
ws://10.60.27.1:5000/ws/holograms?x=0&y=0&z=0&rad=1&rad=100000000
The easiest way - create a proxy server, which is filtering incorrect query-strings and cutting negative "rad"s.
- Python 3
- DOM Based XSS in a web server, written with in-page javascript execution.
Because of wrong comparing and setting in javascript-file static/js/frontend.js:106-118
when creating an article we can assign any value with prefix 'Current title:'
to the object with id=mid-text
(in navigation bar). And it will be built into the html-page.
Using logical bug we can inject a html tag <script>...</script>
. Possible way do it is using markdown syntax for create link inside an article. Doing requests to the other hosts is forbidden by CORS policy. It means, that we must do request to the out host. For example, we can write script, which build and submit html-form with a private suggestion to ourself with any information from the page (cookies in this case). All markdown text generates into html with some escaping, but it overcomes with adding javascript variables.
Just fix the logical bug.
- NodeJS
- MongoDB
- Elliptical curves
Attack
We know our EC(a, b, p, G, n)
and two points on it:
G
- base point of EC
P
- user’s public key (You can find it before “Librarian's note:” at “Visits journal” page)
P = nG
, where n
is user’s private key (or password for service).
Our EC
has very small p
value. It means that we can try discrete logarithm to solve.
Defense
You need to use one of precompiled good curves.
Attack
There is an vulnerability in ECDSA realisation. Using same k
for signing, but need to use a random one each time.
a) So you can take (m1, m2)
and (s1, s2)
from journal (are located at the end of each note). Compute k
.
b) Use r
(from your own signature. It is same for everyone), s
and m
from needed user’s note to compute his private key.
k = (m1 - m2) * inverse_mod(s1 - s2, n) % n
priv_key = (s3*k - m3) * inverse_mod(r, n) % n
Defense
a) remove constant k
from curve initialisation.
b) generate random k
for each sign (not only when it is undefined).
- C, HTTP API
- JS single-page client.
Service for maintaining and reading channels.
The API consists of the following methods, every of which have corresponding route:
Add channel (name, password) -> channel_id
Add post (channel_id, password, text)
Get channel key (channel_id, password) -> key
Change channel password (channel_id, password, new_password)
View channel posts (channel_id) -> (channel_name, <EncryptedPost>[])
The main data structure in the server is struct Channel
, which is defined in types.h as
struct Channel {
int id;
char name[20];
char password[16];
char *key;
struct Post *posts;
};
When user creates new channel, key
sets to buffer of 128 random bytes. These bytes used to encrypt every Post
stored in the channel. Anyone can read collection of encrypted posts in the channel using view
method. But only user who knows channel password can obtain the key and provide it to readers. Channels stored on the disk in separate files, and there are a cache of 200 last used channels in the memory.
There is a buffer overflow in change_password
method:
void change_password(struct Channel *channel, char *new_password) {
memset(channel->password, 0, sizeof(channel->password));
memcpy(channel->password, new_password, strlen(new_password));
save_channel(channel);
}
any data after channel->password
can be overwritten if user provides new_password
of length more than 16 bytes. When processing HTTP request server uses 20-bytes buffer for new_password
field in struct Request
:
struct Request {
const char *method;
const char *url;
int channel_id;
char name[20];
char password[20];
char new_password[20];
char text[1000];
};
so only lowest 4-byte of key can be affected by overflow.
You can create new channel and override lower bytes of key
pointer using change_password
. All channels from the cache and their keys are stored in dynamic-allocated memory. So getting the key leads to reading of some 128 bytes from heap near the original key. And after reading some amount of heap data you can obtain several Channel
structs from the cache, containing their ids and passwords. This data is sufficient to obtain all posts from the channel. Reading any specific channel is possible if you previously hit this channel using any API method, so it was loaded into the cache.
You could patch the service using one of several ways: for example, changing size of request.new_password
to 16 in the server, or rewriting change_password
using strncpy
.