VYPR
High severityNVD Advisory· Published Jan 8, 2018· Updated Aug 6, 2024

CVE-2014-1859

CVE-2014-1859

Description

NumPy before 1.8.1 uses insecure temporary file handling, allowing local attackers to overwrite arbitrary files via symlink attacks.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

NumPy before 1.8.1 uses insecure temporary file handling, allowing local attackers to overwrite arbitrary files via symlink attacks.

Vulnerability

NumPy versions before 1.8.1 contain insecure temporary file usage in four locations: core/tests/test_memmap.py, core/tests/test_multiarray.py, f2py/f2py2e.py, and lib/tests/test_io.py [1][2][3]. The code creates temporary files using functions like tempfile.mktemp() without proper precautions against symlink attacks, allowing an attacker to predict or control the temporary file path [4].

Exploitation

An attacker with local access can exploit this by watching for the creation of temporary files by NumPy and then placing a symbolic link at the predicted path pointing to an arbitrary target file the attacker wants to overwrite. When NumPy writes to the temporary file, the write is redirected to the target file, overwriting its contents. No special privileges beyond local user access and the ability to create symlinks in the temporary directory are required [1][2][4].

Impact

Successful exploitation allows a local attacker to write to arbitrary files on the system, potentially leading to escalation of privileges, data corruption, or denial of service. The attacker gains the ability to overwrite any file writable by the user running NumPy, such as configuration files, scripts, or other sensitive data [1][2][4].

Mitigation

The issue was fixed in NumPy version 1.8.1, released on 2014-02-09. The fix removed the insecure use of mktemp and switched to safer temporary file creation methods [3]. Users should upgrade to NumPy 1.8.1 or later. No workaround is available for earlier versions; individual scripts may be patched as a temporary measure [4]. The vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog as of now.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
numpyPyPI
< 1.8.11.8.1

Affected products

2

Patches

1
0bb46c1448b0

ENH: remove insecure mktemp use

https://github.com/numpy/numpyJulian TaylorFeb 5, 2014via ghsa
5 files changed · +50 51
  • numpy/core/tests/test_memmap.py+16 18 modified
    @@ -1,7 +1,7 @@
     from __future__ import division, absolute_import, print_function
     
     import sys
    -from tempfile import NamedTemporaryFile, TemporaryFile, mktemp
    +from tempfile import NamedTemporaryFile, TemporaryFile
     import os
     
     from numpy import memmap
    @@ -33,12 +33,11 @@ def test_roundtrip(self):
             assert_array_equal(self.data, newfp)
     
         def test_open_with_filename(self):
    -        tmpname = mktemp('', 'mmap')
    -        fp = memmap(tmpname, dtype=self.dtype, mode='w+',
    -                       shape=self.shape)
    -        fp[:] = self.data[:]
    -        del fp
    -        os.unlink(tmpname)
    +        with NamedTemporaryFile() as tmp:
    +            fp = memmap(tmp.name, dtype=self.dtype, mode='w+',
    +                        shape=self.shape)
    +            fp[:] = self.data[:]
    +            del fp
     
         def test_unnamed_file(self):
             with TemporaryFile() as f:
    @@ -55,17 +54,16 @@ def test_attributes(self):
             del fp
     
         def test_filename(self):
    -        tmpname = mktemp('', 'mmap')
    -        fp = memmap(tmpname, dtype=self.dtype, mode='w+',
    -                       shape=self.shape)
    -        abspath = os.path.abspath(tmpname)
    -        fp[:] = self.data[:]
    -        self.assertEqual(abspath, fp.filename)
    -        b = fp[:1]
    -        self.assertEqual(abspath, b.filename)
    -        del b
    -        del fp
    -        os.unlink(tmpname)
    +        with NamedTemporaryFile() as tmp:
    +            fp = memmap(tmp.name, dtype=self.dtype, mode='w+',
    +                        shape=self.shape)
    +            abspath = os.path.abspath(tmp.name)
    +            fp[:] = self.data[:]
    +            self.assertEqual(abspath, fp.filename)
    +            b = fp[:1]
    +            self.assertEqual(abspath, b.filename)
    +            del b
    +            del fp
     
         def test_filename_fileobj(self):
             fp = memmap(self.tmpfp, dtype=self.dtype, mode="w+",
    
  • numpy/core/tests/test_multiarray.py+3 10 modified
    @@ -2316,12 +2316,11 @@ def setUp(self):
             self.x = rand(shape) + rand(shape).astype(np.complex)*1j
             self.x[0,:, 1] = [nan, inf, -inf, nan]
             self.dtype = self.x.dtype
    -        self.filename = tempfile.mktemp()
    +        self.file = tempfile.NamedTemporaryFile()
    +        self.filename = self.file.name
     
         def tearDown(self):
    -        if os.path.isfile(self.filename):
    -            os.unlink(self.filename)
    -            #tmp_file.close()
    +        self.file.close()
     
         def test_bool_fromstring(self):
             v = np.array([True, False, True, False], dtype=np.bool_)
    @@ -2349,7 +2348,6 @@ def test_roundtrip_file(self):
             y = np.fromfile(f, dtype=self.dtype)
             f.close()
             assert_array_equal(y, self.x.flat)
    -        os.unlink(self.filename)
     
         def test_roundtrip_filename(self):
             self.x.tofile(self.filename)
    @@ -2402,8 +2400,6 @@ def test_file_position_after_fromfile(self):
                     f.close()
                     assert_equal(pos, 10, err_msg=err_msg)
     
    -        os.unlink(self.filename)
    -
         def test_file_position_after_tofile(self):
             # gh-4118
             sizes = [io.DEFAULT_BUFFER_SIZE//8,
    @@ -2431,8 +2427,6 @@ def test_file_position_after_tofile(self):
                 f.close()
                 assert_equal(pos, 10, err_msg=err_msg)
     
    -        os.unlink(self.filename)
    -
         def _check_from(self, s, value, **kw):
             y = np.fromstring(asbytes(s), **kw)
             assert_array_equal(y, value)
    @@ -2535,7 +2529,6 @@ def test_tofile_sep(self):
             s = f.read()
             f.close()
             assert_equal(s, '1.51,2.0,3.51,4.0')
    -        os.unlink(self.filename)
     
         def test_tofile_format(self):
             x = np.array([1.51, 2, 3.51, 4], dtype=float)
    
  • numpy/f2py/f2py2e.py+2 2 modified
    @@ -91,7 +91,7 @@
                        --lower is assumed with -h key, and --no-lower without -h key.
     
       --build-dir <dirname>  All f2py generated files are created in <dirname>.
    -                   Default is tempfile.mktemp().
    +                   Default is tempfile.mkdtemp().
     
       --overwrite-signature  Overwrite existing signature file.
     
    @@ -424,7 +424,7 @@ def run_compile():
             del sys.argv[i]
         else:
             remove_build_dir = 1
    -        build_dir = os.path.join(tempfile.mktemp())
    +        build_dir = tempfile.mkdtemp()
     
         _reg1 = re.compile(r'[-][-]link[-]')
         sysinfo_flags = [_m for _m in sys.argv[1:] if _reg1.match(_m)]
    
  • numpy/f2py/__init__.py+13 13 modified
    @@ -28,20 +28,20 @@ def compile(source,
         from numpy.distutils.exec_command import exec_command
         import tempfile
         if source_fn is None:
    -        fname = os.path.join(tempfile.mktemp()+'.f')
    +        f = tempfile.NamedTemporaryFile(suffix='.f')
         else:
    -        fname = source_fn
    -
    -    f = open(fname, 'w')
    -    f.write(source)
    -    f.close()
    -
    -    args = ' -c -m %s %s %s'%(modulename, fname, extra_args)
    -    c = '%s -c "import numpy.f2py as f2py2e;f2py2e.main()" %s' %(sys.executable, args)
    -    s, o = exec_command(c)
    -    if source_fn is None:
    -        try: os.remove(fname)
    -        except OSError: pass
    +        f = open(source_fn, 'w')
    +
    +    try:
    +        f.write(source)
    +        f.flush()
    +
    +        args = ' -c -m %s %s %s'%(modulename, f.name, extra_args)
    +        c = '%s -c "import numpy.f2py as f2py2e;f2py2e.main()" %s' % \
    +                (sys.executable, args)
    +        s, o = exec_command(c)
    +    finally:
    +        f.close()
         return s
     
     from numpy.testing import Tester
    
  • numpy/lib/tests/test_io.py+16 8 modified
    @@ -4,7 +4,9 @@
     import gzip
     import os
     import threading
    -from tempfile import mkstemp, mktemp, NamedTemporaryFile
    +import shutil
    +import contextlib
    +from tempfile import mkstemp, mkdtemp, NamedTemporaryFile
     import time
     import warnings
     import gc
    @@ -21,6 +23,12 @@
                                     assert_raises, run_module_suite)
     from numpy.testing import assert_warns, assert_, build_err_msg
     
    +@contextlib.contextmanager
    +def tempdir(change_dir=False):
    +    tmpdir = mkdtemp()
    +    yield tmpdir
    +    shutil.rmtree(tmpdir)
    +
     
     class TextIO(BytesIO):
         """Helper IO class.
    @@ -177,14 +185,14 @@ def roundtrip(self, *args, **kwargs):
         @np.testing.dec.slow
         def test_big_arrays(self):
             L = (1 << 31) + 100000
    -        tmp = mktemp(suffix='.npz')
             a = np.empty(L, dtype=np.uint8)
    -        np.savez(tmp, a=a)
    -        del a
    -        npfile = np.load(tmp)
    -        a = npfile['a']
    -        npfile.close()
    -        os.remove(tmp)
    +        with tempdir() as tmpdir:
    +            tmp = open(os.path.join(tmpdir, "file.npz"), "w")
    +            np.savez(tmp, a=a)
    +            del a
    +            npfile = np.load(tmp)
    +            a = npfile['a']
    +            npfile.close()
     
         def test_multiple_arrays(self):
             a = np.array([[1, 2], [3, 4]], float)
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

14

News mentions

0

No linked articles in our index yet.