<?php    // Fritz!Box Anmeldesimulator 2.0 (c) 2021 by Michael Engelke <mengelke.de@gmail.com>
/*

RewriteEngine    on
RewriteRule    .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteRule    ^(cgi-bin/(webcm|system_status)|j(ason|uis)_boxinfo\.xml|login(_sid)?\.lua)$    /intern/fb_tools.php/$0    [QSA,NC,L]

*/
$cfg = array(
    
'self'    => __FILE__,
    
'base'    => preg_replace('/\.php\d*$/','.db',__FILE__),
    
'hash'    => false// 'js/hash.js',
    
'name'    => 'FRITZ!Box 7500',
    
'anex'    => 'B',
    
'lang'    => 'de',
    
'code'    => '049',
    
'hwid'    => 0,
    
'time'    => 20,    // 20 Minuten
    
'itr1'    => 10000,
    
'itr2'    => 1000,
    
'flag'    => 'crashreport',
    
'maca'    => '04:08:15:16:23:42',
    
'fosm'    => '00.07.25',
    
'foss'    => 12345,
    
'firm'    => 'avm',
    
'boot'    => 725,
    
'host'    => 'tonline,Telekom',
    
'info'    => 'HTTPS Access',
    
'auth'    =>array('admin' => 'admin123',
            
'Anonymous' => 'h4<k7h3p14n37',
            
'avm' => 'fritz.box',
            
'Max' => 'mustermann',
            
'Anonymous' => 'h4<k7h3p14n37',
            
'fritz1337' => 'SessionID'),
);
function 
authdigest() {            // HTTP Auth Digest
 
global $cfg;
 
$digest = array();
 
$dig false;
 foreach(array(
'PHP_AUTH_DIGEST','HTTP_AUTHORIZATION','REDIRECT_HTTP_AUTHORIZATION') as $key)
  if(isset(
$_SERVER[$key]))
   
$dig $_SERVER[$key];
 if(
$dig and preg_match_all('/(\w+)\s*=\s*(\\\\?["\']?)(.*?)\2(,\s*|\s*$)/',$dig,$array))
  
$digest array_combine($array[1],$array[3]);
 if(!isset(
$digest['response']) or !isset($cfg['auth'][$digest['username']])
    or 
$digest['response'] != md5(md5("$digest[username]:$cfg[info]:".$cfg['auth'][$digest['username']])
    .
":$digest[nonce]:$digest[nc]:$digest[cnonce]:$digest[qop]:".md5("$_SERVER[REQUEST_METHOD]:$digest[uri]"))) {
  
header("WWW-Authenticate: Digest realm=\"$cfg[info]\", qop=\"auth\", nonce=\"".uniqid()."\", algorithm=MD5");
  
header("HTTP/1.0 401 Unauthorized");
  die(
"<HTML><HEAD><TITLE>401 Unauthorized (ERR_NONE)</TITLE></HEAD>"
    
."<BODY><H1>401 Unauthorized</H1><BR>ERR_NONE<HR><B>Webserver</B> ".date('r')."</BODY></HTML>");
 }
}
function 
randStr($len) {
 
$str "";
 while(
$len--)
  
$str .= dechex(rand(0,15));
 return 
$str;
}
function 
newChallenge($ver=1) {
 global 
$db,$cfg;
 
$chal = ($ver == 2) ? sprintf('2$%d$%s$%d$%s',$cfg['itr1'],randStr(32),$cfg['itr2'],randStr(32)) : randStr(8);
 if(
$id $db->querySingle("select id from [sessions] where [ip] = '$_SERVER[REMOTE_ADDR]'"))
  @
$db->exec("
    update    [sessions]
        set    [time] = datetime('now','localtime'),
            [challenge] = '
$chal',
            [sid] = null,
            [user] = null
    where    id = 
$id
        and (datetime([time]) < datetime('now','localtime') or [block] = 0)"
);
 else {
  @
$db->exec("insert into [sessions] ([time],[ip],[challenge]) values (datetime('now','localtime'),'$_SERVER[REMOTE_ADDR]','$chal')");
  
$id $db->lastInsertRowID();
 }
 return 
$id;
}
if(!
function_exists('hash_pbkdf2')) {    // http://php.net/hash_pbkdf2
 
function hash_pbkdf2($algo$pass$salt$count$length$raw=false) {
  
$algo strtolower($algo);
  if(!
$raw)
   
$length /= 2;
  
$block ceil($length strlen(hash($algo,"",true)));
  
$hash "";
  for(
$i=1$i <= $block$i++) {
   
$last $xorsum hash_hmac($algo,$salt.pack("N",$i),$pass,true);
   for(
$j=1$j $count$j++)
    
$xorsum ^= ($last hash_hmac($algo,$last,$pass,true));
   
$hash .= $xorsum;
  }
  if(
$hash substr($hash,0,$length) and !$raw)
   
$hash bin2hex($hash);
  return 
$hash;
 }
}
if(isset(
$_SERVER['PATH_INFO']) and $url $_SERVER['PATH_INFO'] or $url preg_replace('/[?#].*$/','',$_SERVER['REQUEST_URI'])) {
 if(
$db = new SQLite3($cfg['base'])) {
  if(@
$db->querySingle("select [id] from [sessions] limit 0") === false)
   
$db->exec("
    CREATE TABLE IF NOT EXISTS [sessions] (
        [id]        INTEGER        NOT NULL    PRIMARY KEY AUTOINCREMENT,
        [ip]        VARCHAR(40)    NOT NULL    UNIQUE,
        [time]        DATETIME    NOT NULL,
        [block]        INTEGER        NOT NULL    DEFAULT 0,
        [challenge]    VARCHAR(256)    NULL        UNIQUE,
        [sid]        VARCHAR(16)    NULL        UNIQUE,
        [user]        VARCHAR(32)    NULL);"
);
  @
$db->exec("
    delete from [sessions]
    where    [ip] != '
$_SERVER[REMOTE_ADDR]' and datetime([time],'+1 day') < datetime('now','localtime')
        or [sid] is not null and datetime([time]) < datetime('now','localtime');
    update [sessions]
        set    [challenge] = null
    where    [challenge] and datetime([time],'+
$cfg[time] minutes') < datetime('now','localtime')");
  
$pass $user $sid $id false;
  
$out "" or isset($_SERVER['REDIRECT_URL']) and $self $_SERVER['REDIRECT_URL'] or $self $_SERVER['SCRIPT_NAME'];
  
$https = isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] == 'on';
  
$sid = (isset($_REQUEST['sid']) and preg_match('/^[\da-f]{16}$/i',$_REQUEST['sid'],$var)) ? $var[0] : false;
  foreach(array(
'username' => '/^[ \w,.-]+$/i''password' => '/^[ -~]+$/''response' => '/^([\da-f]+)[$-]([\da-f]+)$/i') as $val => $preg)
   $
$val = ((isset($_REQUEST[$val]) and $var $_REQUEST[$val] or isset($_REQUEST["login:command/$val"]) and $var $_REQUEST["login:command/$val"])
    and 
preg_match($preg,$var,$var)) ? ((count($var) > 1) ? $var $var[0]) : false;
  
$page = ($url == '/cgi-bin/webcm' and isset($_REQUEST['getpage'])) ? basename($_REQUEST['getpage']) : false;
  if(
$username) {
   
$preg '/^'.preg_quote($username).'$/i';
   foreach(
$cfg['auth'] as $key => $var)
    if(
preg_match($preg,$key)) {
     
$user $key;
     
$pass $var;
     break;
    }
  }
  
$version =  (isset($_REQUEST['version']) and preg_match('/^[12]$/i',$_REQUEST['version'],$var)) ? $var[0] : 1;
  
$result "";
  
$head "html";

  if(
$url == '/cgi-bin/system_status') {
   
$https and authdigest();
   
$time = (time() - filectime($cfg['self'])) / 60 60;
   
$year floor($time 365.2425/24);
   
$time -= $year 365.2425 24;
   
$month floor($time 30 24);
   
$time -= $month 30 24;
   
$day floor($time 24);
   
$time -= $day 24;
   
$hour floor($time);
#  $time -= floor($hour);
   
$crc32 hash_file('crc32b',$cfg['self']);
   
$out sprintf("\n<html><body>%s-%s-%02d%02d%02d-%02d%02d%02d-%06o-%06o-856700-%s-%d-%s</body></html>\n",
    
$cfg['name'], $cfg['anex'],
    
$hour,$day,$month,$year,
    (
$cfg['boot'] % 3200) / 32 0, ($cfg['boot'] % 3200) % 32,
    ((
hexdec(substr($crc32,0,4)) ^ 65535) << 2) % 262144hexdec(substr($crc32,4)) ^ 65535,
    
str_replace('.','',$cfg['fosm']),$cfg['foss'],
    
$cfg['firm']);
 }
  elseif(
$url == '/jason_boxinfo.xml') {
   
$https and authdigest();
   
$head "xml";
   
$out "
<j:BoxInfo xmlns:j=\"http://jason.avm.de/updatecheck/\">
<j:Name>
$cfg[name]</j:Name>
<j:HW>
$cfg[hwid]</j:HW>
<j:Version>
$cfg[fosm]</j:Version>
<j:Revision>
$cfg[foss]</j:Revision>
<j:Serial>"
.str_replace(':','',$cfg['maca'])."</j:Serial>
<j:OEM>
$cfg[firm]</j:OEM>
<j:Lang>
$cfg[lang]</j:Lang>
<j:Annex>
$cfg[anex]</j:Annex>
<j:Lab></j:Lab>
<j:Country>
$cfg[code]</j:Country>
<j:Flag>
$cfg[flag]</j:Flag>
<j:UpdateConfig>1</j:UpdateConfig></j:BoxInfo>"
;
  }
  elseif(
$url == '/juis_boxinfo.xml') {
   
$https and authdigest();
   
$fw explode('.',$cfg['fosm']);
   
$flag preg_replace('/.*/',"<q:Flag>\$0</q:Flag>\n",$cfg['flag']);
   
$host explode(',',$cfg['host']);
   
$head "xml";
   
$out "
<e:BoxInfo xmlns:e=\"http://juis.avm.de/updateinfo\" xmlns:q=\"http://juis.avm.de/request\">
<q:Name>
$cfg[name]</q:Name>
<q:HW>
$cfg[hwid]</q:HW>
<q:Major>
$fw[0]</q:Major>
<q:Minor>
$fw[1]</q:Minor>
<q:Patch>
$fw[2]</q:Patch>
<q:Buildnumber>
$cfg[foss]</q:Buildnumber>
<q:Buildtype>1</q:Buildtype>
<q:Serial>"
.str_replace(':','',$cfg['maca'])."</q:Serial>
<q:OEM>
$cfg[firm]</q:OEM>
<q:Lang>
$cfg[lang]</q:Lang>
<q:Country>
$cfg[code]</q:Country>
<q:Annex>
$cfg[anex]</q:Annex>
$flag<q:UpdateConfig>1</q:UpdateConfig>
<q:Provider>
$host[0]</q:Provider>
<q:ProviderName>
$host[1]</q:ProviderName></e:BoxInfo>";
  }
  elseif(
$page == 'hashlib.js') {
   
$head "javascript";
   
$out = (isset($cfg['hash']) and file_exists($cfg['hash'])) ? file_get_contents($cfg['hash'])
    : 
gzinflate(base64_decode(/* zopfli -c --i50000 --deflate hash-lz77.js | base64 -w120 */"
bVEHY6M40/4rSDNgZAmCBNjYWITjeu8V4z3HiwN7Wcgn7C1fnOxff/X26goj5qntq/2dfzwPh1M/Dv5BGHEWk+jYw+uuv2t9s5nCu3a4PXXszLXf6dnHVYkw
C/vhefvm66M/hYdub947+YZzxtgmuv7nJJBs3QXB9TmczjfTyfjnv2EFB3/626wfbn0jDNedq7hkLLAc/+u0KOwhT5iVtv43zty0p7MZnPPjP228Fp14yx6O
o7For68DuY7EWx3lbzev/6Yg7zTn3Xy1CGLFX/8F7/3xeWsx31rMf4B2jyISs5mYfbKfOv1wf/PH86Na/4OnEydhRG8Da8UgRnEUB7G3Ad6J27+yk9bz/Kko
tGRi0F/uT114aPs7f7o6df1Ud40fCcn+nrAYNaVir2U+FHqfj1wfWHVz1AddfRF2L/cHS2g4+rR949d7zhsh7bY4VeRsd86cb/ocfyS3+iCO+DU7YkUPpePd
Wed3m1twMsgPXH//l1jDoxlfvv832/5teEAE6/6O7Y4IjHP2zxDA+cod/15hJCZm3UbsUZSOBkpToIwacD6AYpFYt/byREpKjJXGxKTb0jGDVbBIiiGfwPlN
rdRutBi4nak0sZds1HbprT9wDs4rVjpXE9BnpPJaDjQgfem8a9+A81Kyh/7oG8872Q9AiyURuQmCvKtNw/XJ/rK8vZtap/qkiHBDJt2JriSytfTJ3BRt3lpj
HTi3aqrbolBNURTZ3O+vY69dx0HrxcxTaYqTFxUG6KfUInC63b7JourSAbzDCSm4iyRIFyWt7ElEWV591Zce54AvoHTzbN7aXwX0QjpdNznnZoO11ZqVzlcX
ba194Bu22VhWYzkNc5N5xnLSe14X3p+nzm+tKrUSFog9WsPkdLlMWmsD5FnY4tN2Zr1eA/k9ut+bqf10OPkTuJ+j33KtWKCEYkIuGFv7EQfnO1bSXXgasaN+
dY3fY6DAq7zuUeyf37UmBra2gznoRZoqKXotwf1/Ujr3tgJP+xPvmWtYr/2e2+ASYe9gO/skqKfNRi4ufQP+DXkUB3MAR0hwlog/EOs2PKHDLpe6sZB24QXm
Kl0Ufd5zDvSGmLpvdC8m28zE+SbLUZCKWONyF6+yTK1UnCVzX3pVzAB/s0UEJfk0ynsguT+V7rAztW3K86cdkA0rnZA1wO7D+qn6LgJS4svnKYjLEryPEX8l
9g7IB4geAfIhAf4EVQBkgL8MnkEkRl1LkYpYLEUkKi+y/1IJuRRK2fFKyESoSCRCSputULFYCBkJmQolG2vvc3ezSPOppNsZhB+R/c3kI5KpH4C8T+fgLMj9
JWJH8G5p78rFfKyN7osiabi94knjWaSxTuaG927CMxsal0B/ox3YCDohRQDhD8To2hajRNwIIA304XTXH1rf6iqvvovyEcDz8Fe3pHmVcy0XgJK0esJvoYw+
A/EOTHgepq4/nnwT3o/3PmOsrU0dNQ04LjU++sC7+ljH88Eqa7jv93qwMtl1EPSVc11Vqml2+BmRTXN5qty4adgaOewwR1SAEXrI4IIr8PA7xIQp/APx4+2M
gTxQwIqA8zPl1fuRCHA7o9zhsmEcnM8wAvfHYBKnEq/Yo5i6vYToOgd6BoDYK8VXAA5PmWh13dhkbHf8Rw+/2s7GqhwAuDvqDuyf0VGeRYXJx1rZbbfyRRBH
jI0A77mo3KgRQcps7Db7kp4bG1NhrifbwtCsAX7yghjo/5EgQ0JlggQWTUmAAbwghvMrFV0igN6L41WaLFbLTHF/rGWzK51PdmMd2zxUEkWrdLWMMlxQr8ou
CBBXnkKkbC0ze7JM41WMP4YytbdRpJIV1v4TPnMZs+hzHpaQpwBfU5UugL3RVvvHIcwZYE1DgN8BPYS5JGAzBAzgD3TmqEmnHx6F0RLYtJ0lOSvlVVdzbhrI
XlFgNxTmlNyPr30jwhTYZTsTE14F8irG1C+Dz2IZF33e1T3XptEG5h4t8YXZjOCq4Cg6kTFwMktztC3Y4RqMMOB+R8RRH8Hz3QwvdJFYjGPpgh5YX8jU8/y2
FG5b9yUeeIXLhoP7PQ4Y07QRS7ZDgUJmbDcURREzXHPVCIkbtsL3qbTFCQv9tesP+ljb3TK4Lp2Qc3A8v3RqscDffIl0O1Np6X0weMc6bXZPFdrW8C3+4h7L
7ewzhZ+6OCcyRjpXqtpX25l3LD0PAVTlSUQK+cNVqaOM2QDBbRh7gJSiVXS6dJtNrILTo7hfA6KrRKyY6Ne1XMYqzRK5ikUSqVjFcVJGP6ULFWexjJRQS/tE
nC0zu5ClsS1dRc1jPmPsTw=="
));
  }
  elseif(
$page == 'login.js') {
   
$head "javascript";
   
$out = <<<eof
 document.addEventListener("DOMContentLoaded",function(a,b,c) {
 b = document.getElementsByTagName('form')[0];
 b.username[0].disabled = true;
 b.addEventListener('submit',sendPass,false);
 b.username.addEventListener('change',function(a) {
  document.getElementsByTagName('form')[0].password.value = this[this.selectedIndex].title;
 },false);
 b.loginMode.addEventListener('change',setDiv,false);
 if(c = Hash) {
  if(c.pbkdf2 && c.hmac && c.sha256)
   b.loginMode.value = 20;
  else {
   b.loginMode[3].disabled = true;
   if(c.md5)
    b.loginMode.value = 12;
   else
    b.loginMode[2].disabled = true,
    b.loginMode[1].disabled = true;
  }
  setDiv(0,b.loginMode);
 }
});

function setDiv(a,b) {
 a = b || this;
 for(b=0; b < a.length; b++)
  document.getElementById("h" + a[b].value).style.display = a.selectedIndex == b ? "block" : "none";
}
function sendPass(a,b,c,d,e,f,g,h) {
 b = document.getElementsByTagName('form')[0];
 if(b.username.value && b.password.value) {
  if((c = Hash) && b.loginMode.selectedIndex && (d = b.loginMode.value) > 10 && (e = new XMLHttpRequest())) {
   e.open('GET',d == 11 ? '/cgi-bin/webcm?getpage=..%2fhtml%2flogin_sid.xml' : '/login_sid.lua' + (d == 20 ? '?version=2' : ''),false);
   e.onreadystatechange = function(a,b) {        // Challenge aus XML extrahieren
    b = a.target;
    if(b.readyState == 4 && b.status == 200)
     document.getElementsByTagName('form')[0].challenge.value = b.responseXML.getElementsByTagName('Challenge')[0].firstChild.nodeValue;
   };
   e.send(null);
   if(f = b.challenge.value) {
    h = false;
    if(d < 20 && f.length == 8)
     h = f + '-' + c.md5((f + '-' + b.password.value).replace(/./g,'$&\\0'));
    else if(d == 20 && f.substr(0,2) == '2$' && (g = f.split('$')).length == 5) {
     h = g[4] + '$' + c.pbkdf2('sha256',c.pbkdf2('sha256',unescape(encodeURI(b.password.value)),c.hex(g[2],1),g[1]|0,32,true),c.hex(g[4],1),g[3]|0,64);
    }
    if(h) {            // Response vorhanden
     b.response.value = h;
     b.password.value = '';
     b.password.disabled = true;
     b.response.disabled = false;
     if(d == 11) {        // Alter Response-Login
      b.response.name = 'login:command/response';
      b.username.name = 'login:command/username';
     }
    }

   }
   if(b.password.value)
    a.preventDefault();
  }
  else {            // Plain-Text Login (UNSICHER!!!)
   b.password.name = 'login:command/password';
   b.username.name = 'login:command/username';
  }
  b.loginMode.disabled = true;
 }
 else
  a.preventDefault();
}
eof;
  }
  elseif(
$page == 'source')
   
$out show_source($cfg['self'],true);
  else {
   if(
$pass and $response and $id $db->querySingle("
    select    [id]
    from    [sessions]
    where    [ip] = '
$_SERVER[REMOTE_ADDR]'
        and [sid] is null
        and (datetime([time]) <= datetime('now','localtime') or [block] = 0)
        and [challenge] like '%
$response[1]'")) {
    if(
strlen($response[1]) == and ($chk md5(preg_replace('!.!',"\$0\0","$response[1]-$pass"))) == $response[2]
    or 
strlen($response[1]) == 32 and ($var explode('$',$db->querySingle("select [challenge] from [sessions] where [id] = $id")))
    and 
hash_pbkdf2('sha256',hash_pbkdf2('sha256',$pass,hex2bin($var[2]),$var[1],32,true),hex2bin($var[4]),$var[3],64,false) == $response[2]) {
     
$sid randStr(16);
     @
$db->exec("-- Erfolgreicher Login
    update    [sessions]
        set    [challenge] = null,
            [sid] = '
$sid',
            [user] = '
$user',
            [block] = 0,
            [time] = datetime('now','+
$cfg[time] minutes','localtime')
    where [id] = 
$id");
     
$result .= " ok";
    }
    else {
     @
$db->exec("-- Login abgelehnt mit IP-Blocker
    update    [sessions]
        set    [challenge] = null,
            [user] = '
$user',
            [block] = [block] + 1,
            [time] = datetime('now',(1 << [block])||' seconds','localtime')
    where [id] = 
$id");
     
$result .= " fail";
    }
   }
   elseif(
$password and $pass and !$response and $password == $pass) {
    
$sid randStr(16);
    @
$db->exec("-- Login erfolgreich
    update    [sessions]
        set    [challenge] = null,
            [sid] = '
$sid',
            [user] = '
$user',
            [block] = 0,
            [time] = datetime('now','+
$cfg[time] minutes','localtime')
    where [id] = 
$id");
    
$result .= " ok";
   }
   else {
    
$result .= " no response";
    if(
$data $db->querySingle("
    select    id,
        ifnull(max(strftime('%s',time)-strftime('%s','now','localtime'),0),0) as blocked,
        [block],
        [sid],
        [time]
    from    [sessions]
    where    [ip] = '
$_SERVER[REMOTE_ADDR]'",true)) {
     if(
$data['block'] and $data['blocked'])
      
$result .= " - noch $data[blocked] Sekunden bin ".date('d.m.Y H:i:s',strtotime($data['time']))." blockiert";
     
$id $data['id'];
     if(
$sid = ($sid and $sid == $data['sid']) ? $sid false) {
      @
$db->exec("-- Session-Zeit verlaengern
    update    [sessions]
        set    [time] = datetime('now','+
$cfg[time] minutes','localtime')
    where    [sid] = '
$sid'");
      
$result .= " already logged in";
     }
     else
      
$id false;
     if(
$sid and (isset($_REQUEST['logout']) and $_REQUEST['logout']
    or 
$url == '/cgi-bin/webcm' and isset($_REQUEST['security:command/logout']) and $_REQUEST['security:command/logout'])) {
      @
$db->exec("delete from [sessions] where [sid] = '$sid'");
      
$sid $id false;
      
$result "Sie sind ausgeloggt";
     }
    }
    else
     
$sid false;
   }

   if(
$url == '/login_sid.lua') {
    if(!
$sid)
     
$id newChallenge($version);
    
$head "xml";
    
$out '<?xml version="1.0" encoding="utf-8"?>';
    if(
$data $db->querySingle("
    select    id,
        ifnull(max(strftime('%s',time)-strftime('%s','now','localtime'),0),0) as blocked,
        challenge,
        ifnull(sid,printf('%.16d',0)) as sid
    from [sessions]
    where "
.(($id) ? "[id] = $id"[ip] = '$_SERVER[REMOTE_ADDR]'"),true)) {
    
$users $https "<Users/>" "<Users>".implode('',preg_replace('/^.+$/','<User>$0</User>',array_keys($cfg['auth'])))."</Users>";
    
$out .= "
<SessionInfo>
<SID>
$data[sid]</SID>
<Challenge>
$data[challenge]</Challenge>
<BlockTime>
$data[blocked]</BlockTime>
<Rights/>
$users
</SessionInfo>
"
;
    }
    else
     
$out .= "\n<SessionInfo />\n";
   }
# elseif($url == '/login.lua') {}
   
elseif($page == 'login_sid.xml') {    // Login cgi ab 04.74 (Zwischen 4.74 bis 5.29)
    
if(!$sid)
     
$id newChallenge();
    
$head "xml";
    if(
$data $db->querySingle("
    select    id,
        substr(challenge,-8) as challenge,
        ifnull(sid,printf('%.16d',0)) as sid
    from [sessions]
    where "
.(($id) ? "[id] = $id"[ip] = '$_SERVER[REMOTE_ADDR]'"),true)) {
    
$out "
<SessionInfo>
<iswriteaccess>0</iswriteaccess>
<SID>
$data[sid]</SID>
<Challenge>
$data[challenge]</Challenge>
</SessionInfo>
"
;
    }
    else
     
$out "
<SessionInfo />"
;
   }
  }
 }

 if(!
$out) {


  if(!
$sid) {
   
$users "";
   foreach(
$cfg['auth'] as $u => $p)
    if(
preg_match('/^[ \w,.-]{1,32}\t[ -~]{1,32}$/',"$u\t$p"))
     
$users .= "<option value='$u' title='".htmlentities($p,ENT_QUOTES)."'>$u</option>\n";
   
$users "<select name='username'>\n<option value='' title='' selected>Bitte wählen ...</option>\n$users</select>";
   
$login "<form action='$self' method='post'><table>
<tr><td><small>Login Modus</small><br /><select name='loginMode'>
<option value='10'>Version 1.0 (Plaintext: -OS 4)</option>
<option value='11'>Version 1.1 (md5: OS 4 - OS 5)</option>
<option value='12'>Version 1.2 (md5: OS 5 - OS 7)</option>
<option value='20'>Version 2 (pbkdf2: OS 7.24+)</option>
</select> </td></tr>
<tr><td><small>Benutzername</small><br />
$users</td></tr>
<tr><td><small>Kennwort</small><br />
<input type='password' value='' name='password' maxsize='32' placeholder='Enter Password' /></td></tr>
<tr><td align='right'>
<input type='submit' value='Anmelden' />
<input disabled type='hidden' value='' name='challenge' />
<input disabled type='hidden' value='' name='response' />
<input disabled type='hidden' value='' name='getpage' />
</td></tr>
</table></form>
<div id='h10'>
<h3>Anmeldung in Plaintext (Bis etwa Fritz!OS 4.70)</h3>
<ul>
<li>Das Kennwort per POST (<code>login:command/password=&lt;Kennwort&gt;</code>) an <code>http://fritz.box/cgi-bin/webcm</code> schicken
<li>Eine einfache eindeutige Bestätigung gibt es nicht
</ul>
</div>
<div id='h11'>
<h3>Anmeldung mit MD5 Challenge (ab etwa Fritz!OS 4.74 bis Fritz!OS 5.29)</h3>
<ul>
<li>Zuerst mit GET <code>http://fritz.box/cgi-bin/webcm?getpage=../html/login_sid.xml</code> die Challenge anfordern
<li>In der XML-Datei die Challenge auslesen (z.B. <code>12345678</code>)
<li>Mit der Challenge das Kennwort erweitern (z.B. <code>12345678-Kennwort</code>)
<li>Alle Zeichen die Unicode sind (Also überhalb von ASCII-Zeichen > 255) mit "
." ersetzen (z.B. <code>1aa2bß3c? => 1a.2b.3c.</code>)
<li>Nach JEDES Zeichen ein Nullbyte hinzufügen (z.B. <code>1\02\03\04\05\06\07\08\0-\0K\0e\0n\0n\0w\0o\0r\0t\0</code>)
<li>Das ganze mit MD5 Hashen (z.B. <code>c2e5f1cd27636fead3d069734f577800</code>)
<li>Wieder mit der Challenge erweitern (z.B. <code>12345678-c2e5f1cd27636fead3d069734f577800</code>)
<li>Als POST (<code>login:command/response=12345678-c2e5f1cd27636fead3d069734f577800&getpage=../html/login_sid.xml</code>) an <code>http://fritz.box/cgi-bin/webcm</code> schicken und SID anfordern 
<li>In der XML-Datei die SID auslesen (z.B. <code>0123456789abcdef</code>)
<li>Bei JEDEN Request egal ob GET/POST die SID mitschicken (z.b. <code>http://fritz.box/cgi-bin/webcm?sid=0123456789abcdef</code>)
<li>Zwischen zwei Requests darf die Zeitspanne nicht größer als 10 Minuten sein, sonst muss man sich neu anmelden!
<li>Zum Abmelden einfach per POST das Kommando <code>security:command/logout</code> senden (z.B. <code>security:command/logout=1&sid=0123456789abcdef</code>)
</ul>
</div>
<div id='h12'>
<h3>Anmeldung mit MD5 Challenge (ab Fritz!OS 5.30)</h3>
<ul>
<li>Zuerst mit GET <code>http://fritz.box/login_sid.lua</code> die Challenge anfordern
<li>In der XML-Datei die Challenge auslesen (z.B. <code>12345678</code>)
<li>Mit der Challenge das Kennwort erweitern (z.B. <code>12345678-Kennwort</code>)
<li>Alle Zeichen die Unicode sind (Also überhalb von ASCII-Zeichen > 255) mit "
." ersetzen (z.B. <code>1aa2bß3c? => 1a.2b.3c.</code>)
<li>Nach JEDES Zeichen ein Nullbyte hinzufügen (z.B. <code>1\x002\x003\x004\x005\x006\x007\x008\x00-\x00K\x00e\x00n\x00n\x00w\x00o\x00r\x00t\x00</code>)
<li>Das ganze mit MD5 Hashen (z.B. <code>c2e5f1cd27636fead3d069734f577800</code>)
<li>Wieder mit der Challenge erweitern (z.B. <code>12345678-c2e5f1cd27636fead3d069734f577800</code>)
<li>Als POST (<code>response=12345678-c2e5f1cd27636fead3d069734f577800</code>) an <code>http://fritz.box/login_sid.lua</code> schicken und SID anfordern 
<li>In der XML-Datei die SID auslesen (z.B. <code>0123456789abcdef</code>)
<li>Bei JEDEN Request egal ob GET/POST die SID mitschicken (z.b. <code>http://fritz.box/?sid=0123456789abcdef</code>)
<li>Zwischen zwei Requests darf die Zeitspanne nicht größer als 10 Minuten sein, sonst muss man sich neu anmelden!
<li>Zum Abmelden einfach per POST das Kommando <code>security:command/logout</code> senden (z.B. <code>security:command/logout=1&sid=0123456789abcdef</code>)
</ul>
</div>
<div id='h20'>

<h3>Anmeldung mit pbdkf2 Challenge (ab Fritz!OS 7.25)</h3>
<ul>
<li>Zuerst mit GET <code>http://fritz.box/login_sid.lua?version=2</code> die Challenge anfordern
<li>In der XML-Datei die Challenge auslesen (z.B. <code>2\$10000\$00112233445566778899aabbccddeeff\$1000\$0f1e2d3c4b5a69788796a5b4c3d2e1f0</code>)
<li>Prüfen ob die Challenge mit 2\$ beginnt, sonst Fallback zu MD5-Challenge
<li>Challenge am '\$' Zeichen auftrennen und zu ein Array umwandeln (z.B. <code>{0:2, 1:10000 2:'00112233445566778899aabbccddeeff', 3:1000, 4:'0f1e2d3c4b5a69788796a5b4c3d2e1f0'}</code>)
<li>Den Hexwert aus der Challenge-Array[2] in Charcode auflösen <code>'00112233445566778899aabbccddeeff' zu \"\\x00\\x11\\x22\\x33\\x44\\x55\\x66\\x77\\x88\\x99\\xaa\\xbb\\xcc\\xdd\\xee\\xff\"</code>

<li>Mit der Funktion <code>pbdkf2_hmac_sha256(&lt;password&gt;, &lt;salt1&gt;, &lt;iter1&gt;)</code> das Kennwort Hashen <code>pbdkf2_hmac_sha256('admin123', \"\\x00\\x11\\x22\\x33\\x44\\x55\\x66\\x77\\x88\\x99\\xaa\\xbb\\xcc\\xdd\\xee\\xff\", 10000) -&gt; '2d1ba0cef3c0c812548b52b45f888b375abd4ea29fecb1f3cdc984bc9f603f07'</code>

<li>Den Hexwert wieder in Charcode auflösen <code>'2d1ba0cef3c0c812548b52b45f888b375abd4ea29fecb1f3cdc984bc9f603f07' zu \"\\x2d\\x1b\\xa0\\xce\\xf3\\xc0\\xc8\\x12\\x54\\x8b\\x52\\xb4\\x5f\\x88\\x8b\\x37\\x5a\\xbd\\x4e\\xa2\\x9f\\xec\\xb1\\xf3\\xcd\\xc9\\x84\\xbc\\x9f\\x60\\x3f\\x07\"</code>
<li>Den Hexwert aus der Challenge-Array[4] in Charcode auflösen <code>'0f1e2d3c4b5a69788796a5b4c3d2e1f0' zu \"\\x0f\\x1e\\x2d\\x3c\\x4b\\x5a\\x69\\x78\\x87\\x96\\xa5\\xb4\\xc3\\xd2\\xe1\\xf0\"</code>
<li>Nun alles mit der Funktion <code>pbdkf2_hmac_sha256(&lt;hash&gt;, &lt;salt2&gt;, &lt;iter2&gt;)</code> Hashen -&gt; 
pbdkf2_hmac_sha256(\"\\x2d\\x1b\\xa0\\xce\\xf3\\xc0\\xc8\\x12\\x54\\x8b\\x52\\xb4\\x5f\\x88\\x8b\\x37\\x5a\\xbd\\x4e\\xa2\\x9f\\xec\\xb1\\xf3\\xcd\\xc9\\x84\\xbc\\x9f\\x60\\x3f\\x07\", \"\\x0f\\x1e\\x2d\\x3c\\x4b\\x5a\\x69\\x78\\x87\\x96\\xa5\\xb4\\xc3\\xd2\\xe1\\xf0\", 1000) -&gt; '2b098be3549b2d30abc03bdad4ccb87f844b25d12e29e8c0428266130af1dc6b'
<li>Hash-Wert mit Hexwert aus Challenge-Array[4] erweitern '0f1e2d3c4b5a69788796a5b4c3d2e1f0\$2b098be3549b2d30abc03bdad4ccb87f844b25d12e29e8c0428266130af1dc6b'
<li>Per POST response='0f1e2d3c4b5a69788796a5b4c3d2e1f0\$2b098be3549b2d30abc03bdad4ccb87f844b25d12e29e8c0428266130af1dc6b'&username=admin an http://fritz.box/login_sid.lua schicken


</div>
"
;
  }
  else
   
$login " Sie sind eingeloggt!";


  
$out "<!DOCTYPE html>
<html><head><title>
$cfg[name]</title>
<style>
select, input[type=text] { width:100%; }
</style>
<script type='text/javascript' language='javascript' src='/cgi-bin/webcm?getpage=../js/hashlib.js'></script>
<script type='text/javascript' language='javascript' src='/cgi-bin/webcm?getpage=../js/login.js'></script>
</head><body><h1>
$cfg[name] (Anmeldesimulator 2.0 Alpha)</h1>
<table align='right'><tr><td><ul>
<li><a href='http://home.mengelke.de:8080/cgi-bin/'>Fritz!Box Anmeldesimulator 1.0</a>
<li><a href='/fritz.box"
.($sid "?sid=$sid"")."'>fritz.box</a>
<li><a href='/cgi-bin/system_status'>system_status</a>
<li><a href='/jason_boxinfo.xml'>jason_boxinfo.xml</a>
<li><a href='/juis_boxinfo.xml'>juis_boxinfo.xml</a>
<li><a href='/cgi-bin/webcm?getpage=../html/login_sid.xml"
.($sid "&amp;sid=$sid"")."'>login_sid.xml</a>
<li><a href='/login.lua"
.($sid "?sid=$sid"")."'>login.lua</a>
<li><a href='/login_sid.lua"
.($sid "?sid=$sid"")."'>login_sid.lua</a>
<li><a href='/login_sid.lua?version=2"
.($sid "&amp;sid=$sid"")."'>login_sid.lua?version=2</a>
<li><a href='/fritzbox?logout=1"
.($sid "&amp;sid=$sid"")."'>fritzbox?logout=1</a>
</li></ul>
</td></tr></table>

$result
$login

<pre>"
.htmlspecialchars(print_r(compact(explode(',','_GET,_POST,_COOKIE,_REQUEST,https,url,self,cfg,result,response,sid,username,password,user,pass,id')),true),ENT_QUOTES)."</pre><hr>
<b style='float:right'>&copy; "
.date('Y')." <a href='https://www.mengelke.de' target='_blank'>MEngelke.de</a></b>
<b><a href='/cgi-bin/webcm?getpage=source'>Webserver</a></b> "
.date('r')."</body></html>";
 }
 
header("Content-Type: text/$head");
 echo 
$out;
}

?>