Sådan skriver du regler for Checkmarx uden at blive skør

Hej Habr!

I vores arbejde beskæftiger vores virksomhed sig meget ofte med forskellige statiske kodeanalyseværktøjer (SAST). Ud af boksen fungerer de alle gennemsnitligt. Det hele afhænger selvfølgelig af projektet og de teknologier, der bruges i det, samt hvor godt disse teknologier er omfattet af analysereglerne. Efter min mening er et af de vigtigste kriterier, når du vælger et SAST-værktøj, muligheden for at tilpasse det til dine applikationers specifikationer, nemlig at skrive og ændre analyseregler eller, som de oftere kaldes, Custom Queries.

Sådan skriver du regler for Checkmarx uden at blive skør

Vi bruger oftest Checkmarx - en meget interessant og kraftfuld kodeanalysator. I denne artikel vil jeg fortælle om min erfaring med at skrive analyseregler for det.

indholdsfortegnelse

Indrejse

Til at begynde med vil jeg gerne anbefale en af ​​de få artikler på russisk om funktionerne ved at skrive forespørgsler til Checkmarx. Den blev udgivet på Habré i slutningen af ​​2019 under titlen: "Hej, Checkmarx!" Sådan skriver du en Checkmarx SAST-forespørgsel og finder fede sårbarheder.

Den undersøger i detaljer, hvordan man skriver de første forespørgsler i CxQL (Checkmarx Query Language) til nogle testapplikationer og viser de grundlæggende principper for, hvordan analyseregler fungerer.

Jeg vil ikke gentage, hvad der er beskrevet i det, selvom nogle kryds stadig vil være til stede. I min artikel vil jeg forsøge at kompilere en slags "samling af opskrifter", en liste over løsninger på specifikke problemer, som jeg stødte på under mit arbejde med Checkmarx. Jeg var nødt til at tude over mange af disse problemer. Nogle gange var der ikke nok information i dokumentationen, og nogle gange var det endda svært at forstå, hvordan man gør det, der kræves. Jeg håber, at min erfaring og søvnløse nætter ikke vil være forgæves, og denne "samling af Custom Queries-opskrifter" vil spare dig for et par timer eller et par nerveceller. Så lad os begynde!

Generel information om reglerne

Lad os først se på nogle få grundlæggende begreber og processen med at arbejde med reglerne, for at få en bedre forståelse af, hvad der vil ske næste gang. Og også fordi dokumentationen ikke siger noget om dette eller er meget spredt i strukturen, hvilket ikke er særlig praktisk.

  1. Reglerne anvendes under scanning afhængigt af den forudindstilling, der blev valgt ved start (et sæt aktive regler). Du kan oprette et ubegrænset antal forudindstillinger, og præcis hvordan man strukturerer dem afhænger af din process specifikationer. Du kan gruppere dem efter sprog eller vælge forudindstillinger for hvert projekt. Antallet af aktive regler påvirker hastigheden og nøjagtigheden af ​​scanningen.

    Sådan skriver du regler for Checkmarx uden at blive skørOpsætning af Preset i Checkmarx-grænsefladen

  2. Reglerne redigeres i et særligt værktøj kaldet CxAuditor. Dette er en desktop-applikation, der opretter forbindelse til en server, der kører Checkmarx. Dette værktøj har to funktionsmåder: redigering af regler og analyse af resultaterne af en allerede udført scanning.

    Sådan skriver du regler for Checkmarx uden at blive skørCxAudit interface

  3. Regler i Checkmarx er opdelt efter sprog, det vil sige, at hvert sprog har sit eget sæt forespørgsler. Der er også nogle generelle regler, der gælder uanset sprog, det er de såkaldte basisforespørgsler. For det meste involverer grundlæggende forespørgsler at søge efter information, som andre regler bruger.

    Sådan skriver du regler for Checkmarx uden at blive skørInddeling af regler efter sprog

  4. Reglerne er "Executable" og "Non-Executable" (Executed and Not Executed). Ikke helt det rigtige navn, efter min mening, men det er hvad det er. Den nederste linje er, at resultatet af udførelse af "Eksekverbare"-regler vil blive vist i scanningsresultaterne i brugergrænsefladen, og "Ikke-kørbare"-regler er kun nødvendige for at bruge deres resultater i andre anmodninger (i det væsentlige kun en funktion).

    Sådan skriver du regler for Checkmarx uden at blive skørBestemmelse af regeltypen ved oprettelse

  5. Du kan oprette nye regler eller supplere/omskrive eksisterende. For at omskrive en regel skal du finde den i træet, højreklikke og vælge "Tilsidesæt" fra rullemenuen. Det er vigtigt at huske her, at de nye regler ikke i første omgang er inkluderet i forudindstillingerne og ikke er aktive. For at begynde at bruge dem skal du aktivere dem i menuen "Preset Manager" på instrumentet. Omskrevne regler bevarer deres indstillinger, det vil sige, hvis reglen var aktiv, forbliver den sådan og vil blive anvendt med det samme.

    Sådan skriver du regler for Checkmarx uden at blive skørEksempel på en ny regel i Preset Manager-grænsefladen

  6. Under udførelsen bygges et "træ" af anmodninger, som afhænger af hvad. Reglerne, der indsamler information, udføres først, og dem, der bruger dem, dernæst. Udførelsesresultatet cachelagres, så hvis det er muligt at bruge resultaterne af en eksisterende regel, så er det bedre at gøre det, dette vil reducere scanningstiden.

  7. Regler kan anvendes på forskellige niveauer:

  • For hele systemet - vil blive brugt til enhver scanning af ethvert projekt

  • På teamniveau (Team) - vil kun blive brugt til at scanne projekter i det valgte team.

  • På projektniveau - Vil blive anvendt i et specifikt projekt

    Sådan skriver du regler for Checkmarx uden at blive skørBestemmelse af det niveau, reglen vil blive anvendt på

"Ordbog" for begyndere

Og jeg vil starte med et par ting, der gav mig spørgsmål, og jeg vil også vise en række teknikker, der vil forenkle livet markant.

Operationer med lister

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

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

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

Alle fundne varer

Inden for det scannede sprog kan du få en liste over absolut alle elementer, som Checkmarx har identificeret (strenge, funktioner, klasser, metoder osv.). Dette er et rum af objekter, der kan tilgås gennem All. Det vil sige at søge efter et objekt med et bestemt navn searchMe, kan du f.eks. søge efter navn på tværs af alle fundne objekter:

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

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

Men hvis du har brug for at søge på et andet sprog, som af en eller anden grund ikke var inkluderet i scanningen (f.eks. groovy i et Android-projekt), kan du udvide vores objektrum med en variabel:

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

Funktioner til flowanalyse

Disse funktioner bruges i mange regler, og her er et lille snydeark over, hvad de betyder:

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

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

Henter filnavn/sti

Der er flere attributter, der kan hentes fra resultaterne af en forespørgsel (navnet på den fil, hvori posten blev fundet, streng osv.), men dokumentationen fortæller ikke, hvordan man opnår og bruger dem. Så for at gøre dette skal du have adgang til LinePragma-egenskaben, og de objekter, vi har brug for, vil være placeret inde 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 værd at huske på FileName indeholder faktisk stien til filen, da vi brugte metoden GetFirstGraph.

Udførelsesresultat

Der er en speciel variabel inde i CxQL result, som returnerer resultatet af udførelse af din skrevne regel. Det initialiseres med det samme, og du kan skrive mellemresultater ind i det, ændre og forfine dem, mens du arbejder. Men hvis der ikke er nogen tildeling til denne variabel eller funktion inde i reglen return— udførelsesresultatet vil altid være nul.

Følgende forespørgsel vil ikke returnere noget til os som følge af udførelsen og vil altid være tom:

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

Men efter at have tildelt udførelsesresultatet til det magiske variabelresultat, vil vi se, hvad dette kald returnerer til os:

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

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

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

Brug af resultaterne af andre regler

Regler i Checkmarx kan kaldes analoge med funktioner i et almindeligt programmeringssprog. Når du skriver en regel, kan du godt bruge resultaterne af andre forespørgsler. For eksempel er det ikke nødvendigt at søge efter alle metodekald i koden hver gang, bare kald den ønskede regel:

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

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

Denne tilgang giver dig mulighed for at forkorte koden og reducere regeludførelsestiden betydeligt.

Problemløsning

Logning

Når du arbejder med værktøjet, er det nogle gange ikke muligt straks at skrive den ønskede forespørgsel, og du skal eksperimentere og prøve forskellige muligheder. I et sådant tilfælde giver værktøjet logning, som kaldes som følger:

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

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

Men det er værd at huske, at denne metode kun accepteres som input snor, så det vil ikke være muligt at vise en komplet liste over fundne elementer som et resultat af den første operation. Den anden mulighed, som bruges til debugging, er at tildele en magisk variabel fra tid til anden result resultatet af forespørgslen og se, hvad der sker. Denne tilgang er ikke særlig praktisk; du skal være sikker på, at der ikke er nogen tilsidesættelser eller operationer med dette i koden efter result eller bare kommenter koden nedenfor. Eller du kan, ligesom jeg, glemme at fjerne flere sådanne opkald fra en færdiglavet regel og undre dig over, hvorfor intet virker.

En mere bekvem måde er at kalde metoden return med den nødvendige parameter. I dette tilfælde vil udførelsen af ​​reglen afsluttes, og vi vil være i stand til at se, hvad der skete som et resultat af det, vi skrev:

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

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

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

Login problem

Der er situationer, hvor du ikke kan få adgang til CxAudit-værktøjet (som bruges til at skrive regler). Der kan være mange årsager til dette, herunder nedbrud, pludselige Windows-opdateringer, BSOD og andre uforudsete situationer, som er uden for vores kontrol. I dette tilfælde er der nogle gange en uafsluttet session i databasen, som forhindrer dig i at logge ind igen. For at rette det skal du køre flere forespørgsler:

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 efter 8.6:

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

Skriveregler

Nu kommer vi til den mest interessante del. Når du begynder at skrive regler i CxQL, mangler du ofte ikke så meget dokumentation, men nogle levende eksempler på at løse bestemte problemer og beskrive processen med forespørgselsoperation generelt.

Jeg vil forsøge at gøre livet lidt lettere for dem, der begynder at dykke ned i forespørgselssproget og give flere eksempler på brug af Custom Queries til at løse visse problemer. Nogle af dem er ret generelle og kan bruges i din virksomhed praktisk talt uden ændringer, andre er mere specifikke, men de kan også bruges ved at ændre koden, så den passer til dine applikationer.

Så her er de problemer, vi stødte på oftest:

Formål: Der er flere flows i resultaterne af udførelse af reglen, og en af ​​dem er en indlejring af en anden, du skal forlade en af ​​dem.

opløsning: Nogle gange viser Checkmarx faktisk flere datastrømme, der kan overlappe og være en forkortet version af andre. Der er en særlig metode til sådanne tilfælde Reducer Flow. Afhængigt af parameteren vil den vælge det korteste eller længste flow:

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

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

Formål: Udvid listen over følsomme data, som værktøjet reagerer på

opløsning: Checkmarx har grundlæggende regler, hvis resultater bruges af mange andre forespørgsler. Ved at supplere nogle af disse regler med data, der er specifikke for din applikation, kan du straks forbedre dine scanningsresultater. Nedenfor er et eksempel på en regel for at komme i gang:

Liste over generel_privatlivsovertrædelse

Lad os tilføje flere variabler, der bruges i vores applikation til at gemme følsomme oplysninger:

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

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

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

Formål: Udvid listen over variabler med adgangskoder

opløsning: Jeg vil anbefale, at du straks er opmærksom på den grundlæggende regel for definition af adgangskoder i kode og tilføjer en liste over variabelnavne, der er almindeligt anvendte i din virksomhed.

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);
	}
}

Formål: Tilføj brugte rammer, der ikke understøttes af Checkmarx

opløsning: Alle forespørgsler i Checkmarx er opdelt efter sprog, så du skal tilføje regler for hvert sprog. Nedenfor er nogle eksempler på sådanne regler.

Hvis der bruges biblioteker, der supplerer eller erstatter standardfunktionalitet, kan de nemt tilføjes til grundreglen. Så vil alle, der bruger det, straks lære om de nye introduktioner. Som et eksempel er biblioteker til logning i Android Timber og Loggi. I grundpakken er der ingen regler for at identificere ikke-systemopkald, så hvis en adgangskode eller sessionsidentifikator kommer ind i loggen, ved vi ikke om det. Lad os prøve at tilføje definitioner af sådanne metoder til Checkmarx-reglerne.

Testkodeeksempel, der bruger Timber-biblioteket til logning:

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 anmodning om Checkmarx, som giver dig mulighed for at tilføje en definition af at kalde Timber-metoder som et udgangspunkt for data fra applikationen:

Find Android-udgange

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

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

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

Og du kan også tilføje til naboreglen, men denne relaterer direkte til at logge på Android:

FindAndroidLog_Outputs

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

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

Også, hvis Android-applikationer bruger Arbejdsleder ved asynkront arbejde er det en god idé yderligere at informere Checkmarx om dette ved at tilføje en metode til at hente data fra opgaven getInputData:

FindAndroidRead

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

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

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

Formål: Søger efter følsomme data i plist til iOS-projekter

opløsning: iOS bruger ofte specielle filer med .plist-udvidelsen til at gemme forskellige variabler og værdier. Det anbefales ikke at gemme adgangskoder, tokens, nøgler og andre følsomme data i disse filer, da de kan udtrækkes fra enheden uden problemer.

Plist-filer har funktioner, der ikke er tydelige for det blotte øje, men som er vigtige for Checkmarx. Lad os skrive en regel, der vil søge efter de data, vi har brug for, og fortælle os, om adgangskoder eller tokens er nævnt et sted.

Et eksempel på en sådan fil, som indeholder et token til kommunikation 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 nuancer, der skal tages i betragtning, 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);

Formål: Finde oplysninger i XML

opløsning: Checkmarx har meget praktiske funktioner til at arbejde med XML og søge efter værdier, tags, attributter og mere. Men desværre var der en fejl i dokumentationen, hvorfor ikke et eneste eksempel virker. På trods af at denne defekt er blevet elimineret i den seneste version af dokumentationen, skal du være forsigtig, hvis du bruger tidligere versioner af dokumenter.

Her er et forkert eksempel fra dokumentationen:

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

Som et resultat af udførelsesforsøget vil vi modtage en fejl, der All der er ingen sådan metode... Og det er sandt, da der er et særligt, separat objektrum til at bruge funktioner til at arbejde med XML - cxXPath. Sådan ser den korrekte forespørgsel ud for at finde en indstilling i Android, der tillader brugen af ​​HTTP-trafik:

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

Lad os se lidt mere detaljeret på det, da syntaksen for alle funktioner er ens, efter du har fundet en, så skal du bare vælge den du skal bruge. Så sekventielt i henhold til parametrene:

  • "*.xml"— maske af filer, der skal søges i

  • 8 — id for det sprog, reglen anvendes på

  • "cleartextTrafficPermitted"— attributnavn i xml

  • "true" — værdien af ​​denne egenskab

  • false — brug af regulære udtryk ved søgning

  • true — betyder, at søgningen vil blive udført under ignorering af store og små bogstaver, dvs

Som et eksempel brugte vi en regel, der identificerer forkerte, set fra et sikkerhedssynspunkt, netværksforbindelsesindstillinger i Android, der tillader kommunikation med serveren via HTTP-protokollen. Eksempel på en indstilling, der indeholder en attribut cleartextTrafficPermitted med betydningen 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>

Formål: Begræns resultater ved filnavn/sti

opløsning: I et af de store projekter relateret til udviklingen af ​​en mobilapplikation til Android, stødte vi på falske positiver af reglen, der bestemmer sløringsindstillingen. Faktum er, at reglen ud af boksen søger i filen build.gradle en indstilling, der er ansvarlig for at anvende sløringsregler for udgivelsesversionen af ​​applikationen.

Men i store projekter er der nogle gange underordnede filer build.gradle, som henviser til de biblioteker, der indgår i projektet. Det særlige er, at selvom disse filer ikke angiver behovet for sløring, vil indstillingerne for den overordnede assembly-fil blive anvendt under kompileringen.

Opgaven er således at afskære triggere i underordnede filer, der hører til biblioteker. De kan identificeres ved tilstedeværelsen af ​​linjen apply 'com.android.library'.

Eksempel kode fra fil build.gradle, som bestemmer behovet for slø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 inkluderet i projektet, som ikke har denne indstilling:

apply plugin: 'android-library'

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

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

Og reglen 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 tilgang kan være ret universel og nyttig, ikke kun for Android-applikationer, men også for andre tilfælde, hvor du skal afgøre, om et resultat tilhører en bestemt fil.

Formål: Tilføj understøttelse af et tredjepartsbibliotek, hvis syntaksen ikke er fuldt understøttet

opløsning: Antallet af forskellige rammer, der bruges i processen med at skrive kode, er simpelthen ude af diagrammet. Checkmarx kender selvfølgelig ikke altid til deres eksistens, og vores opgave er at lære den at forstå, at visse metoder specifikt hører til denne ramme. Nogle gange kompliceres dette af det faktum, at frameworks bruger funktionsnavne, der er meget almindelige, og det er umuligt entydigt at bestemme forholdet mellem et bestemt kald og et specifikt bibliotek.

Vanskeligheden er, at syntaksen af ​​sådanne biblioteker ikke altid genkendes korrekt, og du skal eksperimentere for at undgå at få et stort antal falske positiver. Der er flere muligheder for at forbedre scanningsnøjagtigheden og løse problemet:

  • Den første mulighed, vi ved med sikkerhed, at biblioteket bruges i et specifikt projekt og kan anvende reglen på teamniveau. Men hvis teamet beslutter sig for at tage en anden tilgang eller bruger flere biblioteker, hvor funktionsnavne overlapper hinanden, kan vi få et ikke særlig behageligt billede af adskillige falske positiver

  • Den anden mulighed er at søge efter filer, hvori biblioteket er tydeligt importeret. Med denne tilgang kan vi være sikre på, at det bibliotek, vi har brug for, er nøjagtigt brugt i denne fil.

  • Og den tredje mulighed er at bruge de to ovenstående tilgange sammen.

Lad os som et eksempel se på et bibliotek, der er velkendt i snævre kredse glat for programmeringssproget Scala, nemlig funktionaliteten Splejsning af bogstavelige værdier. Generelt skal du bruge operatoren for at sende parametre til en SQL-forespørgsel $, som erstatter data i en forudformet SQL-forespørgsel. Det vil sige, at det faktisk er en direkte analog til Prepared Statement i Java. Men hvis du har brug for dynamisk at konstruere en SQL-forespørgsel, for eksempel hvis du skal sende tabelnavne, kan du bruge operatoren #$, som direkte erstatter dataene i forespørgslen (næsten som strengsammenkædning).

Eksempelkode:

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

Checkmarx ved endnu ikke, hvordan man registrerer brugen af ​​splejsning af bogstavelige værdier og springer operatører over #$, så lad os prøve at lære det at identificere potentielle SQL-injektioner og fremhæve de rigtige steder 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));
}

Formål: Søg efter brugte sårbare funktioner i Open Source-biblioteker

opløsning: Mange virksomheder bruger Open Source-overvågningsværktøjer (OSA-praksis) til at opdage brugen af ​​sårbare versioner af biblioteker i udviklede applikationer. Nogle gange er det ikke muligt at opdatere et sådant bibliotek til en sikker version. I nogle tilfælde er der funktionelle begrænsninger, i andre er der slet ingen sikker version. I dette tilfælde vil en kombination af SAST- og OSA-praksis hjælpe med at fastslå, at de funktioner, der fører til udnyttelse af sårbarheden, ikke bruges i koden.

Men nogle gange, især når man overvejer JavaScript, er dette måske ikke en helt triviel opgave. Nedenfor er en løsning, måske ikke ideel, men fungerer ikke desto mindre ved at bruge eksemplet med sårbarheder i komponenten lodash i metoder template и *set.

Eksempler på test af potentielt 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 forbinder 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 leder efter alle vores sårbare metoder, som er opført i sårbarheder:

// Ищем все строки: в которых встречается строка 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));

Formål: Søger efter certifikater, der er integreret i applikationen

opløsning: Det er ikke ualmindeligt, at applikationer, især mobile, bruger certifikater eller nøgler til at få adgang til forskellige servere eller verificere SSL-pinning. Fra et sikkerhedsperspektiv er det ikke den bedste praksis at gemme sådanne ting i kode. Lad os prøve at skrive en regel, der vil søge efter 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;

Formål: At finde kompromitterede tokens i applikationen

opløsning: Det er ofte nødvendigt at tilbagekalde kompromitterede tokens eller andre vigtige oplysninger, der er til stede i koden. Det er selvfølgelig ikke en god idé at gemme dem inde i kildekoden, men situationerne varierer. Takket være CxQL-forespørgsler er det ret nemt at finde ting som dette:

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

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

Konklusion

Jeg håber, at denne artikel vil være nyttig for dem, der begynder deres bekendtskab med Checkmarx-værktøjet. Måske vil de, der har skrevet deres egne regler i lang tid, også finde noget brugbart i denne guide.

Desværre mangler der i øjeblikket en ressource, hvor der kan hentes nye ideer under udviklingen af ​​regler for Checkmarx. Det er derfor, vi har skabt repository på Github, hvor vi vil poste vores arbejde, så alle der bruger CxQL kan finde noget brugbart i det, og også har mulighed for at dele deres arbejde med fællesskabet. Depotet er i gang med at udfylde og strukturere indhold, så bidragydere er velkomne!

Tak for din opmærksomhed!

Kilde: www.habr.com

Tilføj en kommentar