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.
Pessoal, bom dia.
Com relação ao Assunto 2, estou tentando usar o zAtuSenha conforme fonte acima e está dando errorlog. Conseguem ajudar?
[Info – 12:41:36 PM] [12:41:36] INFO: TDS-DA being initialized.
[Info – 12:41:36 PM] [12:41:36] INFO: Application Server connected.
[Info – 12:41:37 PM] [12:41:37] INFO: TDS-DA ready.
[Info – 12:41:57 PM] [12:41:57] ERROR: erro no parâmetroMPUserFormModel: Problem in usr/grp/rule model integrity.
ACTIVATE – MPUSERFORMMODEL.PRW(118) – Params: LCOPY:NIL )
LOADUSER – MPUSERPERSIST.PRW(1241) – Params: XCODE:002693,LDESTROY:NIL,LCHKSUM:.F. )
GETVALUEBYID – MPUSERPERSIST.PRW(715) – Params: CCODE:002693,CSUBMODEL:DATAUSER,CCAMPO:USR_NEEDROLE,LALL:.F. )
MPDBUSER – SPFFUNTIONSDB.PRW(858) – Params: CUSERID:002693,CGROUP:DATAUSER,CTAG:USR_NEEDROLE,LALL:.F. )
FWSFUSER – SIGAPSW.PRG(7744) – Params: CUSERID:002693,CGROUP:DATAUSER,CTAG:USR_NEEDROLE,LALL:.F.,NFORCEFOUND:0 )
GETPAPERS – FWUSERACCOUNT.PRX(3034) – Params: LPRIORITY:.F. )
FWSFLOADPAPER – CFGA550.PRW(426) – Params: CPAPER:,LONLYUSER:.T. )
FWUSERACTIVATE – FWUSERACCOUNTDATA.PRX(150) – Params: OMODEL:O )
{|O| FWUSERACTIVATE(O)} – FWUSERACCOUNTDATA.PRX(93) – Params: O:O )
ACTIVATE – FWFORMMODEL.PRX(434) – Params: LCOPY:.F. )
ACTIVATE – MPUSERFORMMODEL.PRW(127) – Params: LCOPY:NIL )
U_FATUSENHA – RESETPSW.PRW(25) – Params: CLOGINUSR:TESTEMENUS,CSENHAUSR:cbm2024x )
U_ZTESTAR – RESETPSW.PRW(72)
[Info – 12:41:57 PM] [12:41:57] INFO: SmartClient closed. ExitCode=62097 ExistStatus=CancelExit
[Info – 12:41:57 PM] [12:41:57] INFO: TDS-DA finished.
Bom dia Breno, tudo joia?
Não cheguei a pegar esse erro, mas no seu caso, vi pelo log que você publicou, que o usuário é “TESTEMENUS” e a senha é “cbm2024x”.
Se você for na SYS_USR e dar um select procurando, o usuário realmente é “TESTEMENUS”? Ou é “TesteMenus”? Pois no Seek tem q passar com o mesmo nome gravado no banco de dados.
Fico no aguardo do feedback.
Tenha um ótimo e abençoado feriado e fim de semana.
Um grande abraço.
Daniel, achei interessante este post que fez e estamos tentando desenvolver uma automação no cadastro de usuário. Você saberia informar o nome do model dos grids (abas) que tem na rotina CFGA510, por exemplo a aba de Grupos, Papel de Trabalho, Filiais, Ambientes, Acessos, Impressão e Vinculo Funcional. Também precisaria do model da rotina CFGA500?
Bom dia Breno, tudo joia?
Primeiramente obrigado pelo feedback.
Então, você precisaria interceptar o Model ativo da tela, e depois procurar pelo aAllSubModels, para ver os IDs dos Modelos usados na tela em MVC.
Se for o caso, e você tiver interesse, mês que vem agora, em Dezembro, vai subir um curso na nossa assinatura, que é de Execuções Automáticas, e terá as seguintes aulas que poderão te ajudar:
Aula 09 – Descobrindo se uma tela é em MVC (interceptando o model da tela)
Aula 10 – Analisando o nome dos modelos em MVC de uma tela (aAllSubModels)
Se você já for assinante na Hotmart, e quiser uma prévia de ambas as aulas, nos mande um email avisando que lhe enviaremos os vídeos.
Tenha um ótimo e abençoado feriado e fim de semana.
Um grande abraço.