Common Vulnerabilities In Networking Protocols
EthCC[9] · 2 Apr 2026 · 21:21
Kirk Baird examines vulnerabilities in the networking infrastructure that blockchain protocols depend on. Using findings from audits, the speaker covers flaws in UDP, TCP, WebSockets, HTTP, TLS, gRPC and peer-to-peer communication, connecting protocol security to the behavior of its underlying network stack.
Speakers
Topics
From EthCC[9], hosted by Ethereum France.
Transcript
Beautiful. May I come in? Come in if you like. Someone's just lighting the door now, but let's get it started. So, the talk is common vulnerabilities in networking protocols. Why have I put this together? Because I've reviewed a lot of networking protocols in my life and the same bugs pop up again and again. So, this talk is aimed for both the software developer and the security auditor. So, the software developer knows what not to do and the security auditor knows what to find. Now, this is a quick run-through of some of the topics we'll be talking about. I'm going to be keeping this talk very high-level.
While I will dive into a little bit of the networking protocols and explain how they work, this is mostly aimed at describing the general bug classes that appear rather than teaching you how networking works for the first time. What is Sigma Prime? It's blockchain security company, but it's also a software development company. So, lucky for me, I get to see both sides of this stack. I get to see the software development side and I also get to see the security auditing side. And I've actually had some >> [laughter] >> some some rather recent dealings with the software development side and the security side both coming into into interacting at the at the moment. Now, some of you might be structured learners, some of you might be visual
learners. This talk isn't really tailored to you. This talk is tailored to those people who learn best from unhinged security auditors who have spent eight years in the space and are going to ramble on at you. Now, I briefly explained what this talk is for, but let's kind of start diving into the class of networking bugs here. So, networking bugs really appear as liveness bugs mostly, but they can go deeper into authentication and state transition as well. Um what do I mean by this? While blockchain state transition functions and transaction processing, all of that is very juicy and interesting, it won't work without networking.
Networking is how your blocks and your transactions get from A to B. And so, the most common sort of bugs we see in networking stacks is when these transactional transactions and blocks don't get from A to B. So, it stops. It's a liveness issue uh rather than an authentication or any other type of state transitions. Though, I'll show you a little bit later how some of those do sneak into the networking stack as well. Networks have layers, and this beautiful green man is going to teach us about those. So, looking at the networking stack, um this is a really really egregious picture of what a networking stack might look at. Uh at the very bottom, you got TCP or UDP. These are the actual protocols that send the message from A to B. You can go even
lower into the stack if you want where your internet service provider actually has wires and sends frames to people, but we're going to abstract that away because you'll almost never ever deal with that uh in your day-to-day operations. But TCP and UDP, you do deal with a a lot. TCP is kind of like a postman. Uh he will deliver your letter, and he will hopefully get it to the right spot. And if it goes to the wrong spot, someone might be nice enough to put RTS and it'll eventually get delivered to the right spot. Uh UDP is more like a paperboy who's just flinging papers out wherever he cares, and he doesn't care if it lands on your roof, he doesn't care if it lands in your neighbor's yard. It's It's just flying out there. Maybe the message arrives, maybe the message doesn't. And so, that's kind of the difference
between the two. We usually use UDP for streaming services and stuff where you you really care about the pace, and you don't really care about the accuracy as much. We use TCP a lot more in blockchains and networking and HTTP-style things where it's pretty important that your message gets from A to B. TLS is a security layer. Uh it's called Transport Layer Security. Um it's a protocol that uses public key authentication to allow you to connect to the right person. So this kind of wraps it can wrap both TCP and UDP, but it's probably used more commonly on TCP, but it's an authentication layer. And then on top of that we have like the sort of application layer as it called. That's your HTTP, your web sockets or your GRPC things. Sometimes GRPC does use these
underlying web sockets and HTTP. And then in the blockchain state, this is all wrapped into your node. This is your stack and every other peer out on the network is going to have quite a similar stack to you. Hopefully the exact same stack or you probably can't communicate. Nice. So, what stack what part of that stack do the attackers actually touch? All of it. All of it. Your message gets sent through TCP and attacker can choose exactly that. The TCP unwrap TCP unwraps it, gives it to TLS. The attacker can also touch that.
The TLS unwraps it and sends it up to HTTP or your web sockets or your GRPC or whatever. The attacker gets to touch that. So the attacker has a lot of areas where untrusted input is coming into your program and they can do a lot of damage at each of those areas. If anyone's ever looked at smart contracts, your external inputs, your external functions, these are the juicy ones. These are the ones that the attackers can do the most damage. Your networking stack, the whole stack is essentially that. The whole stack is just external functions that attackers can play with. So, moving on to the first part, parsing, framing, and canonicalization. What this slide is about is when you get that first string of bytes, it's just random bytes into your protocol,
what what do those bytes mean? Are they messages? Where are the messages? Are they meant to be specific structs? This is what parsing and framing does. Parsing is turning these specific messages into a nice struct. We all like object-orientated programming. We all like structs. We all like classes. This is what parsing does. Turn random bytes into a nice structured uint64, turns it into strings and whatever else you need. Framing is about determining a bit more where one message ends and the next starts. It's actually not as easy as you think, but that's that's what it's there for.
Now, what type of bugs do we see in this parsing process? The one that pops up the most is a length prefix bug. So, imagine you've got this whole struct and inside your struct is a string. How do you know where that string starts and stops? You got say uint64, yeah, okay, that's that's eight bytes. Then you got a string. How many bytes is a string? We don't know. So, we almost everyone encodes the length before a string and we say, "Okay, our our string is is 10 bytes." So, we know the next 10 bytes should be all string and we can read that.
But, what happens if a user sends you a malicious length? They can send a really big length, they can send a really small length. Um and these are the types of bugs that pops up. If you send a really really big length, say uh 2 to the 32, someone may go and try and allocate 2 to the 32 bytes as they're about to load a string into memory. That could crash you depending on whether or not you got 4 gigs of RAM free. The next one is index out of bounds. Um if you just say the length is longer than the bytes you said, there's a good chance they're going to try and index at the end of that. Their string is going to panic because you can't index a string past the end of the bounds.
And the most interesting one that we see is actually a little bit more complicated to explain, but basically if you send a really big length and have it overflow earlier into your bytes but decode successfully and then continue back up to where you were again and overflow again, you can actually get yourself into an infinite loop where the parser just continuously parses the same data due to an overflow. So, these are the three most common ones that pop up when you have length prefix prefix types. Now, uh the next one is when you have a really small packet that makes a really big piece of data.
Um for example, if if you have a length that's uh only 4 bytes, it might expand up to something really large. Sometimes these happen in loops, sometimes these happen in nested structs. A lot of times this happens when you have uh types that are okay to be empty. Something like a string is okay to be empty, arrays are okay to be empty, but they still actually take up space in your memory. So, the encoding might be nothing, but when you expand them in memory, they can expand and be quite large. So, this is a bit of a resource attack. Um yeah, if your encoding and decoding isn't the same, obviously, that's not going to work. Someone's going to send you some bytes and you're not going to be able to decode it, so that's just a pain.
Lastly, uh this is probably the most interesting of these. It is when something is non-canonical. What does that mean? It means there's two different encodings for the same object. This is Yeah, I I think the best way to explain this is to give an example of what happens in Solidity's ABI encoding if you just add zeros to the end. It still decodes successfully. It's It's completely happy with that, and it's like, "Okay, yeah, this is the same." So, if we have a uint64, uh which is just 8 bytes, and then we have the same 8 bytes and then add another 10 bytes of zeros, they decode to the same uint64.
Um one of the most interesting bugs I've ever found was during a Filecoin review. They had exactly this problem where you could append to the end. But how they were doing their slashing protection was on the bytes before it was decoded. So, if you send uh one bytes one block like this and another block with appended zeros, Filecoin was treating them as two separate blocks and said, "Hey, this proposer made two separate blocks at the same slot. Slash him." So, you could essentially slash any validator at will as many times as you want by just adding zeros to their blocks.
And luckily in Ethereum we have SSE and RLP, which are both made to be canonical to stop problems like this. But if you look at JSON unmarshals and go or you look at ABI encoding and solidity, they're non-canonical and so you'll have a lot of these sorts of edge cases to look out for. Cool. So, um yeah. State transitions uh actually come into play both inside the networking protocols themselves and inside the blockchains as a whole. One of the big places where we see this sort of bad state machine happening is probably the TLS handshake. You need some syncs and you need some acts and what you can do is just send a message at the wrong time and the protocol gets
confused and if you've not got your error handling working properly, then you're going to end up in some sort of uh malformed state and the attacker's going to have a happy day. Go. Nice. So, uh next up is authentication. So, whenever we receive messages from anyone in the world, yeah, yeah, we want to know who's sending us messages, right? Okay. I don't trust any of you and you probably shouldn't trust me in your network either. So, please make sure you check who is sending them. Um and what we see interestingly is like some people use encryption such as AES and they encrypt it and they're like, "Oh, well, like only only the the real person can encrypt an AES message." It's like, "No, that's not true. Anyone can
encrypt an AES message. It'll just probably be garbage." >> [snorts] >> So, encryption is not uh authentication. You actually have to make sure you have some sort of public key signature scheme that or an HMAC that is ensuring that someone is who they say they are. Uh next up is also on that sort of line is replay attacks. Uh just because someone like signed a message or even or encrypted it as well once doesn't mean the same person's going to spam that same message again and again. So, include some nonces, make sure messages are only passed once, or someone might spend your balance a thousand times.
Um next up is just because it's a public key and a signature doesn't mean it's anyone's specific public key and signature. So, make sure this checks a specific ID. Make sure you've either pre-loaded the TLS 6 certificate into your config, or you have maybe some trusted form like um using a DNS or your ISP can connect to a certificate authority, something like that. Now, uh peer IDs and IP addresses, especially in the world of libp2p, anyone can generate them. They're free. They're easy to make. Uh you can change your IP address at will, as well. So, doing protections against certain IP addresses is nice, but anyone can ask for a new IP address from their IP provider, and so it's not really a form
of security to be like this IP address is naughty, we're going to ban him. Um it's it's really complex process actually trying to ban a specific person if you don't have any sort of uh allow list in in your uh protocol. Nice. Uh this one's a classic one. It happens everywhere, not just networking, but if you have a message and a signature, make sure the signature covers everything. Anything that is not signed with the signature can be front-run, manipulated by attackers, and they can use a valid signature on this dodgy message. Lovely.
Cool. So, another big thing that we like to see in networking protocols is resource exhaustion. There is countless ways to cause resource exhaustion. Um I kind of hinted at it a little bit earlier with an infinite loop or expanding your memory a lot, but another big one that happens is if you have something that is very slow to execute. Some certain types of signature validation or KZG or some heavy cryptography operations can be quite slow, and if you're exposing those to people, they can just smash these operations, and your machine might crawl to a halt. Like, it's not going to stop it's not going to stop but it's going to be really slow and and dysfunctional.
So, you need to make sure that you are doing the heaviest validation last. Do the really light validation first and probably do your authentication first if you can. So, the only people can actually get into this slow processing sections are those who are authenticated and genuine users. Now, unbounded queues. It it's actually a tough one again. Unbounded or bounding a queue. If you bound your queue at some point you just stop receiving messages and that's good in some cases because it stops people spamming you but it means you're probably dropping genuine messages as well. So, maybe we keep an unbounded queue and just like stock messages forever. Okay, then our memory's going to grow unbounded as well and we might run out of memory and
crash. So, picking the right length of queue is very important and having some nice system to decide which message to to drop and when. Do we drop messages at the start of the queue or do we drop the most recent messages or do we just randomly drop messages? That's up to you. I don't have a perfect answer for you. I'm sorry. But you'll have to take this case by case. Decompression bombs again, yeah, this is what we hinted to earlier in passing. When they get really really big from a small payload, that's a decompression bomb.
Long length streams. Make sure you close your connections. A lot of people could just open connections open open open and if you never close them they just sit there just eating resources forever. So, put some timeouts in there. Make sure you don't have anything if you got an HTTP connection, make sure you're not just sitting waiting. If it times out after 10 seconds, good. Kill them. It's not your problem it took too long. Next. And yeah, already hinted at this. Do your expensive verification first before you cheat. P2P specifically. So, the good thing about P2P is that we can send messages to just a couple people, they send it to a few other people, and they send it to a few other people.
Um this is really handy because it means you don't have to connect everyone in the network. Great. But, what happens if someone says something bad that kind of causes all of the other peers to send that message and all the other peers to send that message again? This is what we call like a peer-to-peer explosion. You're just deliberately causing messages to send again and again and again, and then everyone just ends up spamming the same message or slight variations to all its other peers, and the network gets congested. So, don't do that. Um what we what we want to do to stop that is make sure we only ever uh send on valid messages. If you have any sort of invalid message or any message that might cause peers to send more messages, um make sure it is authentic and you're only sending it if you need to send it.
Next up is civil pressure and eclipse risk. These are these are pretty similar things, but if you're in a peer-to-peer state, uh you need to connect other peers. How do you know all of these peers are valid? Um if you're churning through peer count, uh you're changing peers every so often, someone can just keep sending you new peer requests, and eventually they may be all of the peers in your network, and then they can just send you whatever they want. This is what we call an eclipse attack, and again, not easy to stop. You have to really limit how often you change peers, maybe occasionally go back to some bootstrapped trusted node and get a new set of peers periodically, and those tend to be the two best protections against that.
Okay. Uh discovery poisoning is quite a similar thing, but also just spam junk when people like, "Oh, what are your peer list?" And you just send them junk so they're just connecting to other junk. Um we've already talked about gossip amplification, um and now uh inbound junk with uh no preexisting trust relationship. So, it it's it's kind of like a peer scoring metric. Uh if someone's just trusted to you someone's just connected to you, and they're sending you a whole bunch of data. They're probably low on your PS score, so uh um you want to kick them if they send you anything bad immediately.
If you If you If it's a new peer, uh they should be real down low on your scoring, and then you'll kick them as soon as you get anything bad, and that will stop you uh amplifying all these like sort of P2P explosion attacks. Go. This is what we got. So, onto the summary, uh recapping what we've done so far is parsing framing. That's all about getting a message bytes, turning it into um an actual structured message that we can read. This This is really good to hit with fuzzers. Uh this is my favorite thing is just the fuzzing the parsers.
They just take bytes. They're really easy to write fuzzers. You can do it in 5 minutes, and they'll find most of these sorts of panics, index out of bounds, and a whole bunch of those sorts of bugs. Uh really good tool, especially with AI now, it's a lot easier to write these things. So, would 10 out of 10 recommend doing it for your own networking stack, and and do it for any security review you're working on. Authentication should really be drilled into by now. You guys know what authentication is. Come on. Make them. >> [laughter] >> And lastly is these specific P2P attacks. Uh P2P networks are really specific, and it's good idea to keep in Keep in mind some of the attacks that kind of happen when they explode.
>> [applause] [applause] >> Thanks for this presentation. Um anyone have a question? Yes. You mentioned a bunch of examples of like client-server protocols that are popular like HTTP um With peer-to-peer, do you have like a go-to protocol that you're just like, "Yes, use this. This works for a peer-to-peer network?"
Yeah, I mean, the most popularly used one would be libp2p. That's the one that most people use. But, it really depends on the configuration as well. There's so many configurations on that. Like, you can configure that to use HTTP or web sockets or a whole bunch of other random protocol encryptions, passing functions. So, Yeah, inside libp2p, but the configurations themselves really depend on your use case. Is there a question? Yes.
Hey, thanks for the talk. Um, I come from the binary exploitation world um not crypto. Um, when I'm looking at some apps, they have custom protocol implementations. Um, how often are you seeing that in this field? >> [snorts] >> So, it's it's actually probably less common, maybe less common than yours. Um, but it does exist. And I think we've reviewed a few of them over the time. People usually end up implementing common ones, like cores and and stuff like that, rather than developing their own from scratch. But, a fresh implementation has
all the same risks pretty much as developing them from scratch, except the architectural design. Thank you. Another? No? Thank you all. >> [applause] [applause] >> All right, you're all free to go. This is the last talk. >> Hey.