No artigo de hoje vamos conhecer 2 dicas importantes para o ERP Protheus e como se prevenir.
Há um tempo atrás estava conversando com alguns amigos que são analistas internos na TOTVS Matriz, e perguntei se existia algum programa de Bug Hunt, pois eu conhecia 2 assuntos que poderiam ser considerados brechas em segurança.
Apesar de não ter algum programa assim na época (março desse ano), pelo menos na parte de customizações, passei pra eles os detalhes, então pode ser que os dois assuntos abaixo no futuro sejam tratados.
Então, a ideia desse artigo, é mostrar algumas coisas que podem causar impactos na base de vocês e como ajudar na prevenção. Vamos lá.
Assunto 1 – Senha disponível em ponto de entrada
Imagina o seguinte cenário, você é um cliente TOTVS, e que diversos analistas passam pela empresa, alguns de confiança e outros que você possui certo receio.
E se eu te falar que existe um ponto de entrada, que deixa a senha do usuário exposta e disponível? E se o analista em questão, cria uma forma de gravar as senhas como por exemplo usando MemoWrite e depois venda os acessos aos concorrentes ou à outras pessoas interessadas?
O que acontece é que existe um ponto de entrada chamado PswSize, que é acionado logo após fazer o login no sistema (até mesmo na tela nova em PO-UI). Mas nesse p.e., que serve para validar o tamanho da senha, ele vem também o conteúdo dela, então vejam por exemplo o print abaixo:
Perceberam, que no PARAMIXB veio o usuário digitado e a senha digitada. Logo alguém malicioso (que consiga compilar o ponto de entrada na base), poderá ter a senha de diretores, de usuários chave, e de todos que usam o Protheus.
Como podemos prevenir? O primeiro passo é você ver se já existe esse ponto de entrada na sua base, se sim, verifica no fonte o que ele esta fazendo.
Se for o caso, você pode desativar o ponto de entrada conforme descrito nesse tutorial – https://terminaldeinformacao.com/2018/03/06/habilitar-desabilitar-funcoes-protheus/
Se precisarem para testar, abaixo o trecho do ponto de entrada demonstrado:
//Bibliotecas #Include "TOTVS.ch" /*/{Protheus.doc} User Function PswSize Efetua a validação do usuário digitado @type Function @author Atilio @since 18/03/2023 @see https://tdn.totvs.com/pages/releaseview.action?pageId=6815189 /*/ User Function PswSize() Local aRet := PARAMIXB If ! IsBlind() FWAlertInfo(aRet[2], "A senha é:") EndIf Return aRet
Assunto 2 – Resetar senhas através de ExecAuto
Apesar de existir o processo via Configurador de alterar um usuário e resetar a senha. E se um analista criar um fonte com um ExecAuto para redefinir senhas, inclusive a senha do Administrador.
Mas como isso seria possível? Abaixo vamos falar sobre a lógica do funcionamento:
- A função deverá ser acionada no programa inicial (nesse caso u_zTestar)
- Será passado um usuário e senha no RPCSetEnv com privilégio de Admin
- A função zAtuSenha será acionada, passando o login do usuário e a nova senha
- Será carregado o ExecAuto da FWUserAccountData, resetando assim o usuário e senha
Então para o cenário acima, eu estou usando o usuário daniel e a senha tst123 (no RPCSetEnv), e eu resetei a senha do usuário Administrador para teste@2023, conforme fonte abaixo:
//Bibliotecas #Include "TOTVS.ch" #Include "FWMVCDef.ch" /*/{Protheus.doc} User Function zAtuSenha Função para resetar senha de usuários @type Function @author Atilio @since 02/06/2022 /*/ User Function zAtuSenha(cLoginUsr, cSenhaUsr) Local cAliasUsr := "MPUSR_USR" Local lDeuCerto := .F. //Abre a tabela de usuários e posiciona DbSelectArea(cAliasUsr) (cAliasUsr)->(DbSetOrder(2)) // USR_CODIGO If (cAliasUsr)->(MsSeek(cLoginUsr)) //Se não usar o filtro ou tentar usar DbSetFilter no lugar de Set Filter To, ocasiona erro atualizando sempre o primeiro registro Set Filter To &("USR_CODIGO = '" + cLoginUsr + "'") //Ativa o modelo como alteração (CFGA510) oModel := FWLoadModel("FWUSERACCOUNTDATA") oModel:SetOperation(MODEL_OPERATION_UPDATE) oModel:Activate() //Define o campo de senha (nome do campo real é USR_SENHA, mas no model é os abaixo) oModel:SetValue("DATAUSER", "USR_PSW", cSenhaUsr) oModel:SetValue("DATAUSER", "USR_PSWCMP", cSenhaUsr) //Se conseguir validar os dados e realizar o commit If oModel:VldData() .And. oModel:CommitData() lDeuCerto := .T. //Se não, houve erro, incrementa a mensagem Else //Busca o Erro do Modelo de Dados aErro := oModel:GetErrorMessage() //Monta o Texto que será mostrado na tela AutoGrLog("Id do formulário de origem:" + ' [' + AllToChar(aErro[01]) + ']') AutoGrLog("Id do campo de origem: " + ' [' + AllToChar(aErro[02]) + ']') AutoGrLog("Id do formulário de erro: " + ' [' + AllToChar(aErro[03]) + ']') AutoGrLog("Id do campo de erro: " + ' [' + AllToChar(aErro[04]) + ']') AutoGrLog("Id do erro: " + ' [' + AllToChar(aErro[05]) + ']') AutoGrLog("Mensagem do erro: " + ' [' + AllToChar(aErro[06]) + ']') AutoGrLog("Mensagem da solução: " + ' [' + AllToChar(aErro[07]) + ']') AutoGrLog("Valor atribuído: " + ' [' + AllToChar(aErro[08]) + ']') AutoGrLog("Valor anterior: " + ' [' + AllToChar(aErro[09]) + ']') //Mostra a mensagem de Erro MostraErro() EndIf //Desativa o modelo da memória oModel:DeActivate() FreeObj(oModel) DbSelectArea(cAliasUsr) Set Filter To EndIf Return lDeuCerto /*/{Protheus.doc} User Function zTestar Função para testar execuções @type Function @author Atilio @since 20/02/2023 /*/ User Function zTestar() If Select("SX2") <= 0 RPCSetEnv("99", "01", "daniel", "tst123", "", "") EndIf u_zAtuSenha("Administrador", "teste@2023") Return
E como nós poderíamos tratar essa situação? Bom, como a rotina é em MVC, podemos criar um ponto de entrada, que permite a alteração somente via SIGACFG. Mas esse p.e. tem uma particularidade, ele também é acionado na tela de login, e não tem o trecho do tudo ok quando é acionado via ExecAuto (FORMPOS / MODELPOS). Fique atento quando você for montá-lo. Abaixo segue um exemplo:
//Bibliotecas #Include "Totvs.ch" /*/{Protheus.doc} User Function FWUSERACCOUNTDATA Ponto de Entrada do Cadastro de Usuários @author Atilio @since 04/11/2023 @version 1.0 @type function @obs Codigo gerado automaticamente pelo Autumn Code Maker *-------------------------------------------------* Por se tratar de um p.e. em MVC, salve o nome do arquivo diferente, por exemplo, FWUSERAC_pe.prw *-----------------------------------------------* A documentacao de como fazer o p.e. esta disponivel em https://tdn.totvs.com/pages/releaseview.action?pageId=208345968 @see http://autumncodemaker.com /*/ User Function FWUSERACCOUNTDATA() Local aArea := FWGetArea() Local aParam := PARAMIXB Local xRet := .T. Local oObj := Nil Local cIdPonto := "" Local cIdModel := "" //Se veio da tela de login, ignora If FWIsInCallStack("AUTHENTICATION") .Or. FWIsInCallStack("RPCSETENV") // ... Else //Se tiver parametros If aParam != Nil //Pega informacoes dos parametros oObj := aParam[1] cIdPonto := aParam[2] cIdModel := aParam[3] //Antes de gravar a tabela If cIdPonto == "FORMCOMMITTTSPRE" //Se veio pelo configurador, pelo cadastro de usuários, permite prosseguir If FWIsInCallStack("CFGA510") // ... //Se não veio, encerra a execução do programa Else Final("Somente é possível atualizar via SIGACFG") EndIf EndIf EndIf EndIf FWRestArea(aArea) Return xRet
Obs.: Não irei citar aqui as formas e ferramentas que usei para identificar, principalmente do assunto 2 que envolveu várias pesquisas, tentativas e erros.
Obs. 2: Amanhã irei subir um artigo de exemplo, do assunto 2, uma tela para resetar senha de usuários, então fiquem atentos as novidades aqui do site.
Bom pessoal por hoje é só.
Abraços e até a próxima.