In September 2015 I disclosed a remote heap buffer overflow in FreeSWITCH on the Bugtraq mailing list. It was assigned CVE-2015-7392. A few weeks later, Tenable Research took a deeper look and published advisory TRA-2015-05, showing that the original patch was incomplete and the attack surface was bigger than anyone had documented. Eleven years on, it's still one of the cleaner case studies I know for why patching is hard — so here's the story.
The bug: a one-line parsing mistake
FreeSWITCH parsed JSON with a parse_string() function that lived, in slightly different copies, in three files: src/switch_json.c, libs/libks/src/ks_json.c and libs/esl/src/esl_json.c. The function takes a JSON string and converts it into a plain C string, handling escape sequences along the way.
Two design choices combined badly. First, the output buffer was sized by simply counting characters from the opening quote to the closing quote — a reasonable shortcut if every escape sequence shrinks the output, which they normally do. Second, Unicode escapes (\u0001) were handled with sscanf: the parser stepped over the u and asked sscanf to convert the next four hex digits.
The problem: sscanf returns the number of items successfully converted, and the code advanced the pointer by four whenever the return was greater than zero — even if sscanf had only consumed one character. So a JSON string like this made the parser skip past the closing quote and keep writing attacker-controlled bytes beyond the end of the heap buffer:
{ "heap" : "overflow\u1" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa }
That's a classic heap overflow, remotely triggerable wherever attacker-controlled JSON reached the parser, with the potential for arbitrary code execution. The CVE entry for CVE-2015-7392 described it as a heap-based buffer overflow in esl_json.c in FreeSWITCH before 1.4.23 and 1.6.x before 1.6.2, allowing remote attackers to execute arbitrary code.
Disclosure and the first patch
I reported the issue through the usual coordinated channels and disclosed it publicly on Bugtraq on September 14, 2015. FreeSWITCH shipped a patch the same day, applied to all three copies of the vulnerable function. The fix guarded the pointer increment after the u — a sensible-looking change.
But it treated the symptom, not the cause. The real bug was the unconditional four-byte advance after sscanf, and the patched code still allowed the overflow to be reached.
Tenable's follow-up: the patch wasn't enough
In November 2015, Tenable Research (credited to Jacob Baines) published TRA-2015-05 with a careful re-analysis. Their key findings:
- The original CVE description was incomplete. It pointed only at
esl_json.c— which isn't FreeSWITCH itself, but the Event Socket Library that third-party applications use to talk to FreeSWITCH. The vulnerable code also lived in FreeSWITCH's ownswitch_json.c. - The September patch didn't fully fix the issue. The heap overflow was still reachable in FreeSWITCH 1.4.4 through 1.6.2 and the 1.7.0 development branch.
- There were more exploitation vectors than documented — including one that needed nothing more than a single UDP packet.
Tenable's work was assigned CVE-2015-8311, and FreeSWITCH shipped proper fixes in versions 1.4.26 and 1.6.5. Their advisory carries a CVSSv2 base score of 10.0 — as severe as it gets.
The real attack surface
TRA-2015-05 documented four ways to reach the vulnerable parser:
- The FreeSWITCH command line, via the
jsoncommand in mod_commands — local, but a direct path to the bug. - The Event Socket module on TCP 8021 — normally bound to localhost, password-protected and ACL-restricted, but remote if an operator opened it up.
- The Verto module's multicast listener on UDP 1337 — the nasty one. Verto accepted JSON-RPC over multicast UDP, so a single crafted packet could trigger the overflow remotely with no authentication at all.
- Malicious JSON from a server to an ESL client — turning the direction around and attacking the applications that connect to FreeSWITCH, potentially via a man-in-the-middle position.
That third vector is the one that stuck with me. A service reachable by a single unauthenticated UDP packet, with a heap overflow in its JSON parser — exactly the kind of exposure that sits unnoticed in infrastructure nobody thinks to pentest.
Lessons that still apply
I work on the consulting side now rather than publishing research, but this CVE captures a lot of what we still see in client engagements:
- Duplicated code means duplicated bugs. The same flawed function existed in three files. Fixing one copy — or describing only one in the CVE — leaves the others live. When we do secure code review, hunting for copied-and-pasted logic is standard practice for exactly this reason.
- Patches need adversarial review. The first fix looked reasonable and closed the Jira ticket, but it guarded the wrong line. Someone has to ask "does this actually kill the bug class?" — which is what Tenable did, and what a good retest does after remediation.
- Parsers are prime targets. JSON, XML, SIP — anything that turns untrusted text into structured data is where memory bugs live. The same class of mistake shows up in the injection flaws I wrote about in Spotting Injection Vulnerabilities in Code Review.
- Version data matters. "FreeSWITCH before 1.4.23" vs. "still exploitable through 1.6.2" is the difference between a scanner flagging an asset and missing it. Accurate detection depends on researchers doing the follow-up work.
Key takeaways
- CVE-2015-7392 was a remote heap buffer overflow in FreeSWITCH's JSON
parse_string(), which I disclosed on Bugtraq in September 2015. - The root cause:
sscanfconsuming fewer than four characters on a\uescape while the parser always advanced four — a one-line mistake with a critical result. - Tenable's TRA-2015-05 showed the first patch was incomplete, found four exploitation vectors (including unauthenticated UDP), and the issue was fully fixed in FreeSWITCH 1.4.26 and 1.6.5 as CVE-2015-8311.
- The durable lessons: fix the root cause, review the patch, audit duplicated code, and don't leave anything out of scope.