Hvordan skrive regler for Checkmarx og ikke bli gal

Hei Habr!

I vårt arbeid arbeider vårt selskap svært ofte med ulike statiske kodeanalyseverktøy (SAST). Ut av boksen fungerer de alle gjennomsnittlig. Selvfølgelig avhenger alt av prosjektet og teknologiene som brukes i det, samt hvor godt disse teknologiene er dekket av analysereglene. Etter min mening er et av de viktigste kriteriene når du velger et SAST-verktøy muligheten til å tilpasse det til spesifikke applikasjoner, nemlig å skrive og endre analyseregler eller, som de oftere kalles, Custom Queries.

Hvordan skrive regler for Checkmarx og ikke bli gal

Vi bruker oftest Checkmarx - en veldig interessant og kraftig kodeanalysator. I denne artikkelen vil jeg snakke om min erfaring med å skrive analyseregler for det.

innholdsfortegnelsen

Entry

Til å begynne med vil jeg anbefale en av de få artiklene på russisk om funksjonene ved å skrive spørringer for Checkmarx. Den ble publisert på Habré i slutten av 2019 under tittelen: "Hei, Checkmarx!" Hvordan skrive en Checkmarx SAST-spørring og finne kule sårbarheter.

Den undersøker i detalj hvordan du skriver de første spørringene i CxQL (Checkmarx Query Language) for noen testapplikasjoner og viser de grunnleggende prinsippene for hvordan analyseregler fungerer.

Jeg vil ikke gjenta det som er beskrevet i den, selv om noen kryss fortsatt vil være til stede. I artikkelen min vil jeg prøve å kompilere en slags "samling av oppskrifter", en liste over løsninger på spesifikke problemer som jeg møtte under arbeidet med Checkmarx. Jeg måtte gruble over mange av disse problemene. Noen ganger var det ikke nok informasjon i dokumentasjonen, og noen ganger var det til og med vanskelig å forstå hvordan man skulle gjøre det som kreves. Jeg håper min erfaring og søvnløse netter ikke vil være forgjeves, og denne "samlingen av Custom Queries-oppskrifter" vil spare deg for noen timer eller et par nerveceller. Så la oss begynne!

Generell informasjon om reglene

La oss først se på noen få grunnleggende konsepter og prosessen med å jobbe med reglene, for en bedre forståelse av hva som vil skje videre. Og også fordi dokumentasjonen ikke sier noe om dette eller er veldig spredt i strukturen, noe som ikke er veldig praktisk.

  1. Reglene brukes under skanning avhengig av forhåndsinnstillingen som ble valgt ved start (et sett med aktive regler). Du kan lage et ubegrenset antall forhåndsinnstillinger, og nøyaktig hvordan du strukturerer dem avhenger av prosessens spesifikasjoner. Du kan gruppere dem etter språk eller velge forhåndsinnstillinger for hvert prosjekt. Antall aktive regler påvirker hastigheten og nøyaktigheten til skanningen.

    Hvordan skrive regler for Checkmarx og ikke bli galSette opp forhåndsinnstilling i Checkmarx-grensesnittet

  2. Reglene er redigert i et spesialverktøy kalt CxAuditor. Dette er et skrivebordsprogram som kobles til en server som kjører Checkmarx. Dette verktøyet har to driftsmoduser: redigering av regler og analyse av resultatene av en allerede utført skanning.

    Hvordan skrive regler for Checkmarx og ikke bli galCxAudit-grensesnitt

  3. Regler i Checkmarx er delt inn etter språk, det vil si at hvert språk har sitt eget sett med spørringer. Det er også noen generelle regler som gjelder uavhengig av språk, dette er de såkalte grunnleggende spørringene. For det meste innebærer grunnleggende søk å søke etter informasjon som andre regler bruker.

    Hvordan skrive regler for Checkmarx og ikke bli galÅ dele regler etter språk

  4. Reglene er «Kjørbar» og «Ikke-kjørbar» (Utført og ikke utført). Ikke helt riktig navn, etter min mening, men det er det det er. Hovedpoenget er at resultatet av å utføre «Kjørbare»-regler vil vises i skanneresultatene i brukergrensesnittet, og «Ikke-kjørbare»-regler er kun nødvendig for å bruke resultatene i andre forespørsler (i hovedsak bare en funksjon).

    Hvordan skrive regler for Checkmarx og ikke bli galBestemme regeltypen når du oppretter

  5. Du kan lage nye regler eller supplere/omskrive eksisterende. For å skrive om en regel, må du finne den i treet, høyreklikke og velge "Overstyr" fra rullegardinmenyen. Det er viktig å huske her at de nye reglene i utgangspunktet ikke er inkludert i forhåndsinnstillingene og ikke er aktive. For å begynne å bruke dem må du aktivere dem i "Preset Manager"-menyen på instrumentet. Omskrevne regler beholder sine innstillinger, det vil si at hvis regelen var aktiv, vil den forbli slik og vil bli brukt umiddelbart.

    Hvordan skrive regler for Checkmarx og ikke bli galEksempel på en ny regel i Preset Manager-grensesnittet

  6. Under utførelse bygges et "tre" av forespørsler, som avhenger av hva. Reglene som samler inn informasjon blir utført først, og de som bruker den deretter. Utførelsesresultatet bufres, så hvis det er mulig å bruke resultatene av en eksisterende regel, er det bedre å gjøre det, dette vil redusere skannetiden.

  7. Regler kan brukes på forskjellige nivåer:

  • For hele systemet - vil bli brukt til enhver skanning av ethvert prosjekt

  • På teamnivå (Team) - vil kun brukes til å skanne prosjekter i det valgte teamet.

  • På prosjektnivå - Vil bli brukt i et spesifikt prosjekt

    Hvordan skrive regler for Checkmarx og ikke bli galBestemme nivået som regelen skal brukes på

"Ordbok" for nybegynnere

Og jeg vil begynne med noen få ting som satte meg spørsmål, og jeg vil også vise en rekke teknikker som vil forenkle livet betydelig.

Operasjoner med lister

- вычитание одного из другого (list2 - list1)
* пересечение списков (list1 * list2)
+ сложение списков (list1 + list2)

& (логическое И) - объединяет списки по совпадению (list1 & list2), аналогично пересечению (list1 * list2)
| (логическое ИЛИ) - объединяет списки по широкому поиску (list1 | list2)

Со списками не работает:  ^  &&  ||  %  / 

Alle gjenstander som er funnet

Innenfor det skannede språket kan du få en liste over absolutt alle elementer som Checkmarx har identifisert (strenger, funksjoner, klasser, metoder osv.). Dette er et rom med objekter som kan nås gjennom All. Det vil si å søke etter et objekt med et bestemt navn searchMe, kan du søke, for eksempel etter navn, på tvers av alle funnet objekter:

// Такой запрос выдаст все элементы
result = All;

// Такой запрос выдаст все элементы, в имени которых присутствует “searchMe“
result = All.FindByName("searchMe");

Men hvis du trenger å søke på et annet språk som av en eller annen grunn ikke ble inkludert i skanningen (for eksempel groovy i et Android-prosjekt), kan du utvide objektområdet vårt med en variabel:

result = AllMembers.All.FindByName("searchMe");

Funksjoner for strømningsanalyse

Disse funksjonene brukes i mange regler, og her er et lite jukseark for hva de betyr:

// Какие данные second влияют на first.
// Другими словами - ТО (second) что влияет на  МЕНЯ (first).
result = first.DataInfluencedBy(second);

// Какие данные first влияют на second.
// Другими словами - Я (first) влияю на ТО (second).
result = first.DataInfluencingOn(second);

Henter filnavn/bane

Det er flere attributter som kan hentes fra resultatene av en spørring (navnet på filen der oppføringen ble funnet, streng, etc.), men dokumentasjonen sier ikke hvordan du skal skaffe og bruke dem. Så, for å gjøre dette, må du få tilgang til LinePragma-egenskapen og objektene vi trenger vil være plassert inne i den:

// Для примера найдем все методы
CxList methods = Find_Methods();

// В методах найдем по имени метод scope
CxList scope = methods.FindByName("scope");

// Таким образом можо получить путь к файлу
string current_filename = scope.GetFirstGraph().LinePragma.FileName;

// А вот таким - строку, где нашлось срабатывание
int current_line = scope.GetFirstGraph().LinePragma.Line;

// Эти параметры можно использовать по разному
// Например получить все объекты в файле
CxList inFile = All.FindByFileName(current_filename);

// Или найти что происходит в конкретной строке
CxList inLine = inFile.FindByPosition(current_line);

Det er verdt å huske på det FileName inneholder faktisk banen til filen, siden vi brukte metoden GetFirstGraph.

Utførelsesresultat

Det er en spesiell variabel inne i CxQL result, som returnerer resultatet av å utføre den skrevne regelen din. Den initialiseres umiddelbart, og du kan skrive mellomresultater inn i den, endre og avgrense dem mens du jobber. Men hvis det ikke er noen tilordning til denne variabelen eller funksjonen i regelen return— utførelsesresultatet vil alltid være null.

Følgende spørring vil ikke returnere noe til oss som et resultat av utførelse og vil alltid være tom:

// Находим элементы foo
CxList libraries = All.FindByName("foo");

Men etter å ha tildelt utførelsesresultatet til det magiske variabelresultatet, vil vi se hva dette kallet returnerer til oss:

// Находим элементы foo
CxList libraries = All.FindByName("foo");

// Выводим, как результат выполнения правила
result = libraries

// Или еще короче
result = All.FindByName("foo");

Bruke resultatene av andre regler

Regler i Checkmarx kan kalles analoge med funksjoner i et vanlig programmeringsspråk. Når du skriver en regel, kan du godt bruke resultatene av andre søk. For eksempel er det ikke nødvendig å søke etter alle metodeanrop i koden hver gang, bare ring den ønskede regelen:

// Получаем результат выполнения другого правила
CxList methods = Find_Methods();

// Ищем внутри метод foo. 
// Второй параметр false означает, что ищем без чувствительности к регистру
result = methods.FindByShortName("foo", false);

Denne tilnærmingen lar deg forkorte koden og redusere regelutførelsestiden betydelig.

Løsning av problemer

Hogst

Når du arbeider med verktøyet, er det noen ganger ikke mulig å umiddelbart skrive ønsket spørring, og du må eksperimentere og prøve forskjellige alternativer. For et slikt tilfelle gir verktøyet logging, som kalles som følger:

// Находим что-то
CxList toLog = All.FindByShortName("log");

// Формируем строку и отправляем в лог
cxLog.WriteDebugMessage (“number of DOM elements =” + All.Count);

Men det er verdt å huske at denne metoden bare godtas som input streng, så det vil ikke være mulig å vise en fullstendig liste over funnet elementer som et resultat av den første operasjonen. Det andre alternativet, som brukes til feilsøking, er å tilordne en magisk variabel fra tid til annen result resultatet av spørringen og se hva som skjer. Denne tilnærmingen er ikke veldig praktisk; du må være sikker på at det ikke er noen overstyringer eller operasjoner med dette i koden etter result eller bare kommenter koden nedenfor. Eller du kan, som meg, glemme å fjerne flere slike anrop fra en ferdig regel og lure på hvorfor ingenting fungerer.

En mer praktisk måte er å kalle metoden return med den nødvendige parameteren. I dette tilfellet vil utførelsen av regelen avsluttes, og vi vil kunne se hva som skjedde som et resultat av det vi skrev:

// Находим что-то
CxList toLog = All.FindByShortName("log");

// Выводим результат выполнения
return toLog

//Все, что написано дальше не будет выполнено
result = All.DataInfluencedBy(toLog)

Påloggingsproblem

Det er situasjoner når du ikke får tilgang til CxAudit-verktøyet (som brukes til å skrive regler). Det kan være mange årsaker til dette, inkludert krasj, plutselige Windows-oppdateringer, BSOD og andre uforutsette situasjoner som er utenfor vår kontroll. I dette tilfellet er det noen ganger en uferdig økt i databasen, som hindrer deg i å logge på igjen. For å fikse det, må du kjøre flere spørringer:

For Checkmarx før 8.6:

// Проверяем, что есть залогиненые пользователи, выполнив запрос в БД
SELECT COUNT(*) FROM [CxDB].[dbo].LoggedinUser WHERE [ClientType] = 6;
 
// Если что-то есть, а на самом деле даже если и нет, попробовать выполнить запрос
DELETE FROM [CxDB].[dbo].LoggedinUser WHERE [ClientType] = 6;

For Checkmarx etter 8.6:

// Проверяем, что есть залогиненые пользователи, выполнив запрос в БД
SELECT COUNT(*) FROM LoggedinUser WHERE (ClientType = 'Audit');
 
// Если что-то есть, а на самом деле даже если и нет, попробовать выполнить запрос
DELETE FROM [CxDB].[dbo].LoggedinUser WHERE (ClientType = 'Audit');

Skriveregler

Nå kommer vi til den mest interessante delen. Når du begynner å skrive regler i CxQL, er det du ofte mangler ikke så mye dokumentasjon som noen levende eksempler på å løse visse problemer og beskrive prosessen med hvordan spørringer fungerer generelt.

Jeg skal prøve å gjøre livet litt enklere for de som begynner å dykke ned i spørringsspråket og gi flere eksempler på bruk av Custom Queries for å løse visse problemer. Noen av dem er ganske generelle og kan brukes i din bedrift praktisk talt uten endringer, andre er mer spesifikke, men de kan også brukes ved å endre koden slik at den passer til dine applikasjoner.

Så her er problemene vi møtte oftest:

Mål: Det er flere flyter i resultatene av å utføre regelen, og en av dem er en neste av en annen, du må forlate en av dem.

løsning: Noen ganger viser Checkmarx faktisk flere dataflyter som kan overlappe og være en forkortet versjon av andre. Det er en spesiell metode for slike tilfeller Reduser Flow. Avhengig av parameteren, vil den velge den korteste eller lengste flyten:

// Оставить только длинные Flow
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceSmallFlow);

// Оставить только короткие Flow
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceBigFlow);

Mål: Utvid listen over sensitive data som verktøyet reagerer på

løsning: Checkmarx har grunnleggende regler, hvis resultater brukes av mange andre spørringer. Ved å supplere noen av disse reglene med data som er spesifikke for applikasjonen din, kan du umiddelbart forbedre skanneresultatene. Nedenfor er en eksempelregel for å komme i gang:

General_privacy_violation_list

La oss legge til flere variabler som brukes i applikasjonen vår for å lagre sensitiv informasjon:

// Получаем результат выполнения базового правила
result = base.General_privacy_violation_list();

// Ищем элементы, которые попадают под простые регулярные выражения. Можно дополнить характерными для вас паттернами.
CxList personalList = All.FindByShortNames(new List<string> {
	"*securityToken*", "*sessionId*"}, false);

// Добавляем к конечному результату
result.Add(personalList);

Mål: Utvid listen over variabler med passord

løsning: Jeg vil anbefale umiddelbart å ta hensyn til den grunnleggende regelen for å definere passord i kode og legge til en liste over variabelnavn som vanligvis brukes i din bedrift.

Password_privacy_violation_list

CxList allStrings = All.FindByType("String"); 
allStrings.Add(All.FindByType(typeof(StringLiteral))); 
allStrings.Add(Find_UnknownReference());
allStrings.Add(All.FindByType(typeof (Declarator)));
allStrings.Add(All.FindByType(typeof (MemberAccess)));
allStrings.Add(All.FindByType(typeof(EnumMemberDecl))); 
allStrings.Add(Find_Methods().FindByShortName("get*"));

// Дополняем дефолтный список переменных
List < string > pswdIncludeList = new List<string>{"*password*", "*psw", "psw*", "pwd*", "*pwd", "*authKey*", "pass*", "cipher*", "*cipher", "pass", "adgangskode", "benutzerkennwort", "chiffre", "clave", "codewort", "contrasena", "contrasenya", "geheimcode", "geslo", "heslo", "jelszo", "kennwort", "losenord", "losung", "losungswort", "lozinka", "modpas", "motdepasse", "parol", "parola", "parole", "pasahitza", "pasfhocal", "passe", "passord", "passwort", "pasvorto", "paswoord", "salasana", "schluessel", "schluesselwort", "senha", "sifre", "wachtwoord", "wagwoord", "watchword", "zugangswort", "PAROLACHIAVE", "PAROLA CHIAVE", "PAROLECHIAVI", "PAROLE CHIAVI", "paroladordine", "verschluesselt", "sisma",
                "pincode",
								"pin"};
								
List < string > pswdExcludeList = new List<string>{"*pass", "*passable*", "*passage*", "*passenger*", "*passer*", "*passing*", "*passion*", "*passive*", "*passover*", "*passport*", "*passed*", "*compass*", "*bypass*", "pass-through", "passthru", "passthrough", "passbytes", "passcount", "passratio"};

CxList tempResult = allStrings.FindByShortNames(pswdIncludeList, false);
CxList toRemove = tempResult.FindByShortNames(pswdExcludeList, false);
tempResult -= toRemove;
tempResult.Add(allStrings.FindByShortName("pass", false));

foreach (CxList r in tempResult)
{
	CSharpGraph g = r.data.GetByIndex(0) as CSharpGraph;
	if(g != null && g.ShortName != null && g.ShortName.Length < 50)
	{
		result.Add(r);
	}
}

Mål: Legg til brukte rammeverk som ikke støttes av Checkmarx

løsning: Alle søk i Checkmarx er delt inn etter språk, så du må legge til regler for hvert språk. Nedenfor er noen eksempler på slike regler.

Hvis det brukes biblioteker som utfyller eller erstatter standardfunksjonalitet, kan de enkelt legges til grunnregelen. Da vil alle som bruker det umiddelbart lære om de nye introduksjonene. Som et eksempel er biblioteker for logging i Android Timber og Loggi. I grunnpakken er det ingen regler for å identifisere ikke-systemanrop, så hvis et passord eller øktidentifikator kommer inn i loggen, vil vi ikke vite om det. La oss prøve å legge til definisjoner av slike metoder til Checkmarx-reglene.

Testkodeeksempel som bruker Timber-biblioteket for logging:

package com.death.timberdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import timber.log.Timber;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Timber.e("Error Message");
        Timber.d("Debug Message");

        Timber.tag("Some Different tag").e("And error message");
    }
}

Og her er et eksempel på en forespørsel om Checkmarx, som lar deg legge til en definisjon av å kalle Timber-metoder som et utgangspunkt for data fra applikasjonen:

Finn Android-utganger

// Получаем результат выполнения базового правила
result = base.Find_Android_Outputs();

// Дополняем вызовами, которые приходят из библиотеки Timber
CxList timber = All.FindByExactMemberAccess("Timber.*") +
    All.FindByShortName("Timber").GetMembersOfTarget();

// Добавляем к конечному результату
result.Add(timber);

Og du kan også legge til naboregelen, men denne gjelder direkte pålogging på Android:

FindAndroidLog_Outputs

// Получаем результат выполнения базового правила
result = base.Find_Android_Log_Outputs();

// Дополняем вызовами, которые приходят из библиотеки Timber
result.Add(
  All.FindByExactMemberAccess("Timber.*") +
  All.FindByShortName("Timber").GetMembersOfTarget()
);

Også, hvis Android-applikasjoner bruker Arbeidsleder for asynkront arbeid er det lurt å i tillegg informere Checkmarx om dette ved å legge til en metode for å hente data fra oppgaven getInputData:

FinnAndroidRead

// Получаем результат выполнения базового правила
result = base.Find_Android_Read();

// Дополняем вызовом функции getInputData, которая используется в WorkManager
CxList getInputData = All.FindByShortName("getInputData");

// Добавляем к конечному результату
result.Add(getInputData.GetMembersOfTarget());

Mål: Søker etter sensitive data i plist for iOS-prosjekter

løsning: iOS bruker ofte spesielle filer med filtypen .plist for å lagre ulike variabler og verdier. Det anbefales ikke å lagre passord, tokens, nøkler og andre sensitive data i disse filene, da de kan trekkes ut fra enheten uten problemer.

Plist-filer har funksjoner som ikke er åpenbare for det blotte øye, men som er viktige for Checkmarx. La oss skrive en regel som vil søke etter dataene vi trenger og fortelle oss om passord eller tokens er nevnt et sted.

Et eksempel på en slik fil, som inneholder et token for kommunikasjon med backend-tjenesten:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>DeviceDictionary</key>
	<dict>
		<key>phone</key>
		<string>iPhone 6s</string>
	</dict>
	<key>privatekey</key>
	<string>MIICXAIBAAKBgQCqGKukO1De7zhZj6+</string>
</dict>
</plist>

Og en regel for Checkmarx, som har flere nyanser som bør tas i betraktning når du skriver:

// Используем результат выполнения правила по поиску файлов plist, чтобы уменьшить время работы правила и 
CxList plist = Find_Plist_Elements();

// Инициализируем новую переменную
CxList dictionarySettings = All.NewCxList();

// Теперь добавим поиск всех интересующих нас значений. В дальнейшем можно расширять этот список.
// Для поиска значений, как ни странно, используется FindByMemberAccess - поиск обращений к методам. Второй параметр внутри функции, false, означает, что поиск нечувствителен к регистру
dictionarySettings.Add(plist.FindByMemberAccess("privatekey", false));
dictionarySettings.Add(plist.FindByMemberAccess("privatetoken", false));

// Для корректного поиска из-за особенностей структуры plist - нужно искать по типу "If statement"
CxList ifStatements = plist.FindByType(typeof(IfStmt));

// Добавляем в результат, перед этим получив родительский узел - для правильного отображения
result = dictionarySettings.FindByFathers(ifStatements);

Mål: Finne informasjon i XML

løsning: Checkmarx har veldig praktiske funksjoner for å jobbe med XML og søke etter verdier, tagger, attributter og mer. Men dessverre var det en feil i dokumentasjonen på grunn av at ikke et eneste eksempel fungerer. Til tross for at denne feilen er eliminert i den siste versjonen av dokumentasjonen, vær forsiktig hvis du bruker tidligere versjoner av dokumenter.

Her er et feil eksempel fra dokumentasjonen:

// Код работать не будет
result = All.FindXmlAttributesByNameAndValue("*.app", 8, “id”, "error- section", false, true);

Som et resultat av utførelsesforsøket vil vi motta en feilmelding som All det er ingen slik metode... Og dette er sant, siden det er et spesielt, separat objektrom for bruk av funksjoner for å arbeide med XML - cxXPath. Slik ser det riktige søket ut for å finne en innstilling i Android som tillater bruk av HTTP-trafikk:

// Правильный вариант с использованием cxXPath
result = cxXPath.FindXmlAttributesByNameAndValue("*.xml", 8, "cleartextTrafficPermitted", "true", false, true);

La oss se på det litt mer detaljert, siden syntaksen for alle funksjoner er lik, etter at du har funnet ut en, trenger du bare å velge den du trenger. Så, sekvensielt i henhold til parametrene:

  • "*.xml"— maske av filer som skal søkes i

  • 8 — id for språket som regelen gjelder for

  • "cleartextTrafficPermitted"— attributtnavn i xml

  • "true" – verdien av dette attributtet

  • false — bruk av regulære uttrykk ved søk

  • true — betyr at søket vil bli utført uten å ignorere store og små bokstaver, det vil si at det ikke skilles mellom store og små bokstaver

Som et eksempel brukte vi en regel som identifiserer feil, fra et sikkerhetssynspunkt, nettverkstilkoblingsinnstillinger i Android som tillater kommunikasjon med serveren via HTTP-protokollen. Eksempel på en innstilling som inneholder et attributt cleartextTrafficPermitted med mening true:

<network-security-config>
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
        <domain-config cleartextTrafficPermitted="true">
            <domain includeSubdomains="true">secure.example.com</domain>
        </domain-config>
    </domain-config>
</network-security-config>

Mål: Begrens resultater etter filnavn/bane

løsning: I et av de store prosjektene knyttet til utviklingen av en mobilapplikasjon for Android, møtte vi falske positiver av regelen som bestemmer obfuskeringsinnstillingen. Faktum er at regelen ut av boksen søker i filen build.gradle en innstilling som er ansvarlig for å bruke tilsløringsregler for utgivelsesversjonen av applikasjonen.

Men i store prosjekter er det noen ganger underordnede filer build.gradle, som viser til bibliotekene som inngår i prosjektet. Det særegne er at selv om disse filene ikke indikerer behovet for tilsløring, vil innstillingene til den overordnede sammenstillingsfilen bli brukt under kompilering.

Dermed er oppgaven å kutte ut triggere i barnefiler som tilhører biblioteker. De kan identifiseres ved tilstedeværelsen av linjen apply 'com.android.library'.

Eksempelkode fra fil build.gradle, som bestemmer behovet for tilsløring:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"
    defaultConfig {
        ...
    }

    buildTypes {
        release {
            minifyEnabled true
            ...
        }
    }
}

dependencies {
  ...
}

Eksempel på fil build.gradle for et bibliotek inkludert i prosjektet som ikke har denne innstillingen:

apply plugin: 'android-library'

dependencies {
  compile 'com.android.support:support-v4:18.0.+'
}

android {
  compileSdkVersion 14
  buildToolsVersion '17.0.0'
  ...
}

Og regelen for Checkmarx:

ProGuardObfuscationNotInUse

// Поиск метода release среди всех методов в Gradle файлах
CxList releaseMethod = Find_Gradle_Method("release");

// Все объекты из файлов build.gradle
CxList gradleBuildObjects = Find_Gradle_Build_Objects();

// Поиск того, что находится внутри метода "release" среди всех объектов из файлов build.gradle
CxList methodInvokesUnderRelease = gradleBuildObjects.FindByType(typeof(MethodInvokeExpr)).GetByAncs(releaseMethod);

// Ищем внутри gradle-файлов строку "com.android.library" - это значит, что данный файл относится к библиотеке и его необходимо исключить из правила
CxList android_library = gradleBuildObjects.FindByName("com.android.library");

// Инициализация пустого массива
List<string> libraries_path = new List<string> {};

// Проходим через все найденные "дочерние" файлы
foreach(CxList library in android_library)
{
    // Получаем путь к каждому файлу
	string file_name_library = library.GetFirstGraph().LinePragma.FileName;
    
    // Добавляем его в наш массив
	libraries_path.Add(file_name_library);
}

// Ищем все вызовы включения обфускации в релизных настройках
CxList minifyEnabled = methodInvokesUnderRelease.FindByShortName("minifyEnabled");

// Получаем параметры этих вызовов
CxList minifyValue = gradleBuildObjects.GetParameters(minifyEnabled, 0);

// Ищем среди них включенные
CxList minifyValueTrue = minifyValue.FindByShortName("true");

// Немного магии, если не нашли стандартным способом :D
if (minifyValueTrue.Count == 0) {
	minifyValue = minifyValue.FindByAbstractValue(abstractValue => abstractValue is TrueAbstractValue);
} else {
    // А если всё-таки нашли, то предыдущий результат и оставляем
	minifyValue = minifyValueTrue;	
}

// Если не нашлось таких методов
if (minifyValue.Count == 0)
{
    // Для более корректного отображения места срабатывания в файле ищем или buildTypes или android
	CxList tempResult = All.NewCxList();
	CxList buildTypes = Find_Gradle_Method("buildTypes");
	if (buildTypes.Count > 0) {
		tempResult = buildTypes;
	} else {
		tempResult = Find_Gradle_Method("android");
	}
	
	// Для каждого из найденных мест срабатывания проходим и определяем, дочерний или основной файлы сборки
	foreach(CxList res in tempResult)
	{
        // Определяем, в каком файле был найден buildType или android методы
		string file_name_result = res.GetFirstGraph().LinePragma.FileName;
        
        // Если такого файла нет в нашем списке "дочерних" файлов - значит это основной файл и его можно добавить в результат
		if (libraries_path.Contains(file_name_result) == false){
			result.Add(res);
		}
	}
}

Denne tilnærmingen kan være ganske universell og nyttig, ikke bare for Android-applikasjoner, men også for andre tilfeller når du trenger å finne ut om et resultat tilhører en bestemt fil.

Mål: Legg til støtte for et tredjepartsbibliotek hvis syntaksen ikke støttes fullt ut

løsning: Antallet ulike rammeverk som brukes i prosessen med å skrive kode, er rett og slett utenfor listene. Checkmarx vet selvfølgelig ikke alltid om deres eksistens, og vår oppgave er å lære den å forstå at visse metoder tilhører spesifikt dette rammeverket. Noen ganger er dette komplisert av det faktum at rammeverk bruker funksjonsnavn som er svært vanlige og det er umulig å entydig bestemme forholdet til et bestemt kall til et spesifikt bibliotek.

Vanskeligheten er at syntaksen til slike biblioteker ikke alltid gjenkjennes riktig, og du må eksperimentere for å unngå å få et stort antall falske positiver. Det er flere alternativer for å forbedre skanningsnøyaktigheten og løse problemet:

  • Det første alternativet, vi vet med sikkerhet at biblioteket brukes i et spesifikt prosjekt og kan bruke regelen på teamnivå. Men hvis teamet bestemmer seg for å ta en annen tilnærming eller bruker flere biblioteker der funksjonsnavn overlapper, kan vi få et lite hyggelig bilde av mange falske positiver

  • Det andre alternativet er å søke etter filer der biblioteket er tydelig importert. Med denne tilnærmingen kan vi være sikre på at biblioteket vi trenger er nøyaktig brukt i denne filen.

  • Og det tredje alternativet er å bruke de to ovennevnte tilnærmingene sammen.

La oss som et eksempel se på et bibliotek som er kjent i trange sirkler slick for programmeringsspråket Scala, nemlig funksjonaliteten Skjøte bokstavelige verdier. Generelt, for å sende parametere til en SQL-spørring, må du bruke operatoren $, som erstatter data med en forhåndsutformet SQL-spørring. Det vil si at det faktisk er en direkte analog til Prepared Statement i Java. Men hvis du trenger å dynamisk konstruere en SQL-spørring, for eksempel hvis du trenger å sende tabellnavn, kan du bruke operatoren #$, som vil erstatte dataene direkte i spørringen (nesten som strengsammenkobling).

Kodeeksempel:

// В общем случае - значения, контролируемые пользователем
val table = "coffees"
sql"select * from #$table where name = $name".as[Coffee].headOption

Checkmarx vet ennå ikke hvordan man oppdager bruken av Splicing Literal Values ​​og hopper over operatører #$, så la oss prøve å lære den å identifisere potensielle SQL-injeksjoner og fremheve de riktige stedene i koden:

// Находим все импорты
CxList imports = All.FindByType(typeof(Import));

// Ищем по имени, есть ли в импортах slick
CxList slick = imports.FindByShortName("slick");

// Некоторый флаг, определяющий, что импорт библиотеки в коде присутствует
// Для более точного определения - можно применить подход с именем файла
bool not_empty_list = false;
foreach (CxList r in slick)
{
    // Если встретили импорт, считаем, что slick используется
	not_empty_list = true;
}

if (not_empty_list) {
    // Ищем вызовы, в которые передается SQL-строка
	CxList sql = All.FindByShortName("sql");
	sql.Add(All.FindByShortName("sqlu"));
	
	// Определяем данные, которые попадают в эти вызовы
	CxList data_sql = All.DataInfluencingOn(sql);
	
	// Так как синтакис не поддерживается, можно применить подход с регулярными выражениями
	// RegExp стоит использовать крайне осторожно и не применять его на большом количестве данных, так как это может сильно повлиять на производительность
	CxList find_possible_inj = data_sql.FindByRegex(@"#$", true, true, true);

    // Избавляемся от лишних срабатываний, если они есть и выводим в результат
	result = find_possible_inj.FindByType(typeof(BinaryExpr));
}

Mål: Søk etter brukte sårbare funksjoner i åpen kildekode-biblioteker

løsning: Mange selskaper bruker åpen kildekode-overvåkingsverktøy (OSA-praksis) for å oppdage bruken av sårbare versjoner av biblioteker i utviklede applikasjoner. Noen ganger er det ikke mulig å oppdatere et slikt bibliotek til en sikker versjon. I noen tilfeller er det funksjonelle begrensninger, i andre er det ingen sikker versjon i det hele tatt. I dette tilfellet vil en kombinasjon av SAST- og OSA-praksis bidra til å fastslå at funksjonene som fører til utnyttelse av sårbarheten ikke brukes i koden.

Men noen ganger, spesielt når du vurderer JavaScript, er dette kanskje ikke en helt triviell oppgave. Nedenfor er en løsning, kanskje ikke ideell, men fungerer likevel, ved å bruke eksempelet på sårbarheter i komponenten lodash i metoder template и *set.

Eksempler på test av potensielt sårbar kode i en JS-fil:

/**
 * Template example
 */

'use strict';
var _ = require("./node_modules/lodash.js");


// Use the "interpolate" delimiter to create a compiled template.
var compiled = _.template('hello <%= js %>!');
console.log(compiled({ 'js': 'lodash' }));
// => 'hello lodash!'

// Use the internal `print` function in "evaluate" delimiters.

var compiled = _.template('<% print("hello " + js); %>!');
console.log(compiled({ 'js': 'lodash' }));
// => 'hello lodash!'

Og når du kobler direkte i html:

<!DOCTYPE html>
<html>
<head>
    <title>Lodash Tutorial</title>
    <script src="./node_modules/lodash.js"></script>
    <script type="text/javascript">
  // Lodash chunking array
        nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];

        let c1 = _.template('<% print("hello " + js); %>!');
        console.log(c1);

        let c2 = _.template('<% print("hello " + js); %>!');
        console.log(c2);
    </script>
</head>
<body></body>
</html>

Vi ser etter alle våre sårbare metoder, som er oppført i sårbarheter:

// Ищем все строки: в которых встречается строка lodash (предполагаем, что это объявление импорта библиотеки
CxList lodash_strings = Find_String_Literal().FindByShortName("*lodash*");

// Ищем все данные: которые взаимодействуют с этими строками
CxList data_on_lodash = All.InfluencedBy(lodash_strings);


// Задаем список уязвимых методов
List<string> vulnerable_methods = new List<string> {"template", "*set"};

// Ищем все наши уязвимые методы, которые перечисленны в уязвимостях и отфильтровываем их только там, где они вызывались
CxList vulnerableMethods = All.FindByShortNames(vulnerable_methods).FindByType(typeof(MethodInvokeExpr));

//Находим все данные: которые взаимодействуют с данными методами
CxList vulnFlow = All.InfluencedBy(vulnerableMethods);

// Если есть пересечение по этим данным - кладем в результат
result = vulnFlow * data_on_lodash;

// Формируем список путей по которым мы уже прошли, чтобы фильтровать в дальнейшем дубли
List<string> lodash_result_path = new List<string> {};

foreach(CxList lodash_result in result)
{
    // Очередной раз получаем пути к файлам
	string file_name = lodash_result.GetFirstGraph().LinePragma.FileName;
	lodash_result_path.Add(file_name);
}

// Дальше идет часть относящаяся к html файлам, так как в них мы не можем проследить откуда именно идет вызов
// Формируем массив путей файлов, чтобы быть уверенными, что срабатывания уязвимых методов были именно в тех файлах, в которых объявлен lodash
List<string> lodash_path = new List<string> {};
foreach(CxList string_lodash in lodash_strings)
{
	string file_name = string_lodash.GetFirstGraph().LinePragma.FileName;
	lodash_path.Add(file_name);
}

// Перебираем все уязвимые методы и убеждаемся, что они вызваны в тех же файлах, что и объявление/включение lodash
foreach(CxList method in vulnerableMethods)
{
	string file_name_method = method.GetFirstGraph().LinePragma.FileName;
	if (lodash_path.Contains(file_name_method) == true && lodash_result_path.Contains(file_name_method) == false){
		result.Add(method);
	}
}

// Убираем все UknownReferences и оставляем самый "длинный" из путей, если такие встречаются
result = result.ReduceFlow(CxList.ReduceFlowType.ReduceSmallFlow) - result.FindByType(typeof(UnknownReference));

Mål: Søker etter sertifikater innebygd i applikasjonen

løsning: Det er ikke uvanlig at applikasjoner, spesielt mobile, bruker sertifikater eller nøkler for å få tilgang til forskjellige servere eller verifisere SSL-pinning. Fra et sikkerhetsperspektiv er det ikke den beste praksisen å lagre slike ting i kode. La oss prøve å skrive en regel som vil søke etter lignende filer i depotet:

// Найдем все сертификаты по маске файла
CxList find_certs = All.FindByShortNames(new List<string> {"*.der", "*.cer", "*.pem", "*.key"}, false);

// Проверим, где в приложении они используются
CxList data_used_certs = All.DataInfluencedBy(find_certs);

// И для мобильных приложений - можем поискать методы, где вызывается чтение сертификатов
// Для других платформ и приложений могут быть различные методы
CxList methods = All.FindByMemberAccess("*.getAssets");

// Пересечение множеств даст нам результат по использованию локальных сертификатов в приложении
result = methods * data_used_certs;

Mål: Finne kompromitterte tokens i applikasjonen

løsning: Det er ofte nødvendig å tilbakekalle kompromitterte tokens eller annen viktig informasjon som finnes i koden. Det er selvfølgelig ikke en god idé å lagre dem inne i kildekoden, men situasjonene varierer. Takket være CxQL-spørringer er det ganske enkelt å finne ting som dette:

// Получаем все строки, которые содержатся в коде
CxList strings = base.Find_Strings();

// Ищем среди всех строк нужное нам значение. В примере токен в виде строки "qwerty12345"
result = strings.FindByShortName("qwerty12345");

Konklusjon

Jeg håper at denne artikkelen vil være nyttig for de som begynner å bli kjent med Checkmarx-verktøyet. Kanskje vil de som har skrevet sine egne regler lenge også finne noe nyttig i denne veiledningen.

Dessverre er det for tiden mangel på en ressurs hvor nye ideer kan hentes under utviklingen av regler for Checkmarx. Det er derfor vi opprettet repository på Github, hvor vi vil legge ut arbeidet vårt slik at alle som bruker CxQL kan finne noe nyttig i det, og også få muligheten til å dele arbeidet sitt med fellesskapet. Depotet er i ferd med å fylle ut og strukturere innhold, så bidragsytere er velkomne!

Takk for din oppmerksomhet!

Kilde: www.habr.com

Legg til en kommentar