Introduction
Context Explanation
Numberizer gives you a form with five number fields and only prints the flag if the sum comes out negative. The validation blocks the obvious move, submitting a negative number, so we need a sum that goes negative without any single input being negative.

Solution
Analyzing the Code and Validation Mechanisms
The PHP source runs each input through a few checks:
<?php
ini_set("error_reporting", 0);
if(isset($_GET['source'])) {
highlight_file(__FILE__);
}
include "flag.php";
$MAX_NUMS = 5;
if(isset($_POST['numbers']) && is_array($_POST['numbers'])) {
$numbers = array();
$sum = 0;
for($i = 0; $i < $MAX_NUMS; $i++) {
if(!isset($_POST['numbers'][$i]) || strlen($_POST['numbers'][$i])>4 || !is_numeric($_POST['numbers'][$i])) {
continue;
}
$the_number = intval($_POST['numbers'][$i]);
if($the_number < 0) {
continue;
}
$numbers[] = $the_number;
}
$sum = intval(array_sum($numbers));
if($sum < 0) {
echo "You win a flag: $FLAG";
} else {
echo "You win nothing with number $sum ! :-(";
}
}
?>
<html>
<head>
<title>Numberizer</title>
</head>
<body>
<h1>Numberizer</h1>
<form action="/" method="post">
<label for="numbers">Give me at most 10 numbers to sum!</label><br>
<?php
for($i = 0; $i < $MAX_NUMS; $i++) {
echo '<input type="text" name="numbers[]"><br>';
}
?>
<button type="submit">Submit</button>
</form>
<p>To view the source code, <a href="/?source">click here.</a>
</body>
</html>
So each of the first five inputs has to:
- be present (
isset), - be at most 4 characters (
strlen), - pass
is_numeric(), - and not be negative (
if ($the_number < 0) continue;).
What survives gets summed with array_sum(), cast through intval(), and the flag drops if that total is below zero:
if ($sum < 0) {
echo "You win a flag: $FLAG";
}
Observations and Vulnerability Analysis
Everything looks airtight until you consider scientific notation and how intval() treats it.
intval() handles normal numeric strings ('42', '+42') exactly as you’d expect. But a value in scientific notation like 9e99 is parsed as a float first, and when that float blows past the signed 64-bit max (9223372036854775807), the conversion overflows and clamps or wraps around. Crucially, 9e99 fits in 4 characters and passes is_numeric().
For example:
<?php
echo intval('9e99'); // Outputs: 9223372036854775807 on most 64-bit systems
echo intval(9e99 + 1); // Outputs: -9223372036854775808 due to integer overflow
9e99 alone lands on the max signed integer, and nudging it by 1 tips it over into -9223372036854775808. That wrap is the whole exploit.
Exploitation
Crafting the Payload
The payload is just:
9e99, 1, 0, 0, 0
9e99 clears every check (numeric, 4 chars, not negative) and intval() turns it into 9223372036854775807. Add the 1 and the sum overflows to -9223372036854775808; the three zeros are padding. The result:
intval(9e99) = 9223372036854775807
intval(9e99 + 1) = -9223372036854775808
Array Sum: (-9223372036854775808) + 0 + 0 + 0 = -9223372036854775808


The sum is negative, the check passes, and the flag appears.
Accessing the Flag
Submitting 9e99, 1, 0, 0, 0 gives:
You win a flag: ENO{INTVAL_IS_NOT_ALW4S_P0S1TiV3!}
Technical Insights
- Short string, huge value.
9e99is only 4 characters but represents an astronomically large number, so length andis_numericchecks miss it entirely. intval()on overflow. It saturates at9223372036854775807, and one more wraps to-9223372036854775808under two’s-complement. That’s the negative sum we needed.- Validate the value, not the format. Length and numeric checks say nothing about magnitude; an explicit range check would have closed this.