How to get peer certificate in cowboy_http_handler - ssl

My question for today: is there an official way to obtain peer ssl socket information (peer certificate to be exact) in cowboy_http_handler's Handler:handle(Req, State)?
Of course, I can scrape Req tuple (peer socket is the second field as of today) with erlang:element/2, but this is not future-proof and just doesn't look right.
Thanks in advance!

There is an exported call:
cowboy_req:get(socket, Req)
It returns the socket, or just about everything else there is in the Req object, currently:
bindings
body_state
buffer
connection
headers
host
host_info
meta
method
multipart
onresponse
path
path_info
peer
pid
port
qs
resp_body
resp_compress
resp_headers
resp_state
socket
transport
version
I'm not sure if it is in the documentation, I can't see it, but it's a lot better and less likely to fail than just getting a numbered element value out, and you can always add a unit test that checks that it works, so if it does get stamped/broken at some point you get a heads up. I don't expect it'll go anywhere though.

Related

How to use scapy to decrypt TLS traffic and print the http headers

I want to decrypt TLS traffic in a pcap file using scapy by providing certificate and private key.
But i dont have a clue how to do it without using wireshark or tshark
Could someone give me some suggestions? Thanks in advance.
The three things you might want to look at are:
TLSSession: https://scapy.readthedocs.io/en/latest/usage.html?highlight=tlssession#advanced-sniffing-sniffing-sessions - i.e. how to dissect "on-the-flow" using a session in Scapy
Scapy's TLS notebooks: https://github.com/secdev/scapy/tree/master/doc/notebooks/tls - they have prety detailed explanations and examples regarding dissection/decryption
This test case in scapy: https://github.com/secdev/scapy/blob/3040f6d705176731494a7bcf76b820f077716729/test/tls.uts#L1166-L1218 that puts it all together.
The key steps are (from the last test case):
>>> key = PrivKeyRSA("srv_key.pem")
>>> res = sniff(offline="tls.pcap", session=TLSSession(server_rsa_key=key))
Obviously this only makes sense when using RSA and not Diffie-Hellman. Good luck !

How to put data in gundb at server side as a peer

I thought gun instance in the server was also one of the peers.
But when I put data on the server, the peer can't get the data.
Here is my simple test code.
global.gun.get('servertest').put('yes'); // at server side
gun.get('servertest').once(console.log); // at client side
And it prints undefined.
please, let me know how to use a gun instance on server side.
On the server, run this to actually accept remote connections:
var server = require('http').createServer().listen(8080);
var gun = Gun({web: server});
On the client, run this to connect to your server:
var gun = Gun({peers: ["http://server-ip-or-hostname:8080/gun"]})
As a side note, even if you establish a peer connection to get your data, you still need to handle undefined, as once() might fire several times as data is coming in.
Relevant links:
https://gun.eco/docs/Installation#server:
https://github.com/amark/gun/tree/master/examples
https://github.com/skiqh/gun-cli
EDIT:
To be more explicit about my side note above -- the once callback on your client getting undefined for non-local data is actually by design. It means the client does not have the requested data available yet. It will however request it from its peers, which will try to answer with what they themselves can resolve (locally or from their respective peers). These answers will trigger the callback again (if they got through the CRDT algorithm I think).
Getting undefined on the client could also mean the server's response might have timed out and GUN considered it unanswered. You can prolong the waiting time with .once(callback_function, {wait: time_in_miliseconds}).
As per Hadar's answer, try using on() instead of once() to mitigate race conditions, i.e. your client requesting data from the server before you actually wrote it. Once you got your data and don't want any more updates, you can unsubscribe with gun.get('servertest').off()
Also, it might be noteworthy that GUN instances are not magically linked; having two of them connected does not mean they are one and the same in any way. Conceptually, they are peers in a distributed system, which in GUN's case gives you eventual consistency with all the limits and tradeoffs associated with that.
#skiqh
Hello, Thanks for your answer.
I initiated gun instance well in both server and client.
server
let server = https.createServer(options, app);
server.listen( port );
let gun = Gun({ file: 'data', web: server });
global.gun = gun; // <-- my gun instance on server side
global.gun.get('servertest').put('yes'); <-- I tried to put data
// listening~~~~~
client
window.G = G;
let opt = {};
opt.store = RindexedDB(opt);
opt.localStorage = false;
opt.peers = ['https://my.link/gun'];
G.gun = Gun(opt); // <-- my gun instance on client
gun.get('servertest').once(console.log) // <-- it prints "undefined" even though I put data here by server!
I really want to know how to use methods like .put(), .get(), .on() etc.. on the server side using gun instance.
I tried doing this but failed as I attached the result on my post.
Please, Let me know what Im doing something wrong and the correct way.
Thank you
try gun.on instead of once. on will subscribe to all changes.
your example should work if you run .once only after you write something to the server.
using gun.on on client should work regardless and will trigger the moment the server write somehting

Wireshark dissector that works with tls/ssl

I have a protocol that uses SSL/TLS over a non-standard port and transmits non-standard data (not http) through it. I'm trying to make a wireshark dissector (in lua) to dissect this protocol.
How do I do this? I can register a dissector that gets called for tcp fragments on that port
local dissector_table_tcp = DissectorTable.get("tcp.port")
dissector_table_tcp:add(1234, myprotocol)
I can get the SSL dissector to then decode all the fragments as SSL
function myprotocol.dissector(tvb, pinfo, root)
local ssl_dissector = Dissector.get("ssl")
local ssl_dissected_len = ssl_dissector:call(tvb, pinfo, root)
pinfo.cols.protocol:set("My Protocol")
At this point, if I have a premaster key file set in Wireshark (Preferences->Protocols->SSL->Master key file), I can see the decrypted contents of the packets and all is good. Sort of.
But I want to create fields for my protocol and put them in the protocol tree. How do I get at the decrypted data that the ssl dissector produced?
Update:
I'm trying to muddle through this as best as I can; there's no tutorial on how exactly you're supposed to do this. It sort of looks like Wireshark has a programming model based on fields/variables that are populated by dissectors, and that in theory it should be possible to interrogate those variables to find the output of a dissector.
To that end, I've been running the SSL dissector and then looking at fields that it declares, but it doesn't actually seem to populate them. When I run a post-dissector after the SSL dissector, none of the seemingly-useful fields, like ssl.segments or ssl.segment.data, are set:
protocol_foo = Proto("foo", "Foo protocol")
port = 4172
g_field_segment = Field.new("ssl.segment")
g_field_segment_data = Field.new("ssl.segment.data")
g_field_segments = Field.new("ssl.segments")
g_field_reassembled_data = Field.new("ssl.reassembled.data")
function protocol_foo.dissector(tvb, pinfo, root)
print("====== protocol_foo")
for k,v in pairs({ g_field_segment, g_field_segment_data, g_field_segments, g_field_reassembled_data }) do
if v() ~= nil then
print("Field " .. v.name .. " is NOT nil")
else
print("Field " .. v.name .. " is nil")
end
end
end
-- post-dissector registration
local ssl_dissector = Dissector.get("ssl")
local dissector_table_tcp = DissectorTable.get("tcp.port")
dissector_table_tcp:add(port, ssl_dissector)
register_postdissector(protocol_foo)
When I run this code on my protocol, none of those ssl.segment* variables test positive; lots of variables (like the ssl.handshake.*) variables do test positive (at least with handshake pdus), but not the ones with the decrypted contents.
Does anyone have any ideas?

OpenSSL: Specify packet size of application data

When I do something like this, apps/openssl s_client -connect 10.102.113.3:443 -ssl3, client-server communication is created using openSSL.
Now, I want to send application data from the client to the server. For example, after doing apps/openssl s_client -connect 10.30.24.45:443 -ssl3, I get something like this:
...certificate and session details...
---
GET /path/to/file
The GET /path/to/file all goes in one SSL record. I want to send it in multiple records.
I assume I have to edit apps/s_client.c, and find the place where the SSL_write or similar happens.
How do I go about something like that?
For a properly designed application the TCP packet sizes and SSL frame sizes should not matter. But there are badly designed applications out there which expect to get like the HTTP request inside a single read, which often means that it must be inside the same SSL frame. If you want to run tests against applications to check for this kind of behavior you either have to patch your s_client application or you might use something else, like
#!/usr/bin/perl
use strict;
use IO::Socket::SSL;
my $sock = IO::Socket::SSL->new('www.example.com:443') or die "$!,$SSL_ERROR";
print $sock "GE";
print $sock "T / HT";
print $sock "TP/1.0\r\n\r\n";
This will send the HTTP request header within 3 SSL frames (which might still get put together into the same TCP packet). Since on lots of SSL stacks (like OpenSSL) one SSL_read reads only a single SSL frame this will result in 3 reads necessary to read the full HTTP request.
Okay, I figured out that I needed to change the number of bytes I'm writing using the SSL_write.
This is a code snippet, starting at line 1662 of s_client.c:
if (!ssl_pending && FD_ISSET(SSL_get_fd(con),&writefds))
{
k=SSL_write(con,&(cbuf[cbuf_off]), (unsigned int)cbuf_len);
.......
}
To make the application data be sent in multiple records instead of just the one, change the last parameter in the SSL_write.
For example, do this:
if (!ssl_pending && FD_ISSET(SSL_get_fd(con),&writefds))
{
k=SSL_write(con,&(cbuf[cbuf_off]), 1);
.......
}
This will result in something like this:
Notice the multiple records for Application Data instead of just the one.

SSL client-hello, message body structure

I'm trying to work with SSL manually without using libs as OpenSSL etc... And I have fault on 1st step with Hello message from client.
From technet:
It must consist of:
ClientVersion 3,1
ClientRandom[32]
SessionID: None (new session)
Suggested Cipher Suites:
TLS_RSA_WITH_3DES_EDE_CBC_SHA
TLS_RSA_WITH_DES_CBC_SHA
Suggested Compression Algorithm: NONE
In my code ( C/C++ ), I have created message on this way:
char *request = "ClientVersion 3,1\r\nClientRandom[32]\r\n
SessionID: None (new session)\r\n
Suggested Cipher Suites:\r\n
TLS_RSA_WITH_3DES_EDE_CBC_SHA\r\n
TLS_RSA_WITH_DES_CBC_SHA\r\n
Suggested Compression Algorithm: NONE\r\n";
But after recv() functions, I've got 0 in result, so what's wrong in my message structure?
PS
In HTTP-proto there is a place, when must be double \r\n ( which splits the headers and the body message ), may be, there must be something like this or not?
You need to take a good look at RFC 2246, rather than just making things up. For example the newlines between the elements of the message are a figment of your imagination.
But why you think you can work with SSL 'manually' is a mystery. You can't, and you don't need to. Reimplementing SSL is a major task and you don't have the resources to do it. Use your library: OpenSSL, JSSE, etc.