Hur man skriver regler för Checkmarx utan att bli galen

Hej, Habr!

I sitt arbete har vårt företag ofta att göra med olika verktyg för statisk kodanalys (SAST). Ur boxen fungerar de alla medelhavligt. Självklart beror allt på projektet och de teknologier som används i det, samt hur väl dessa teknologier täcks av analysreglerna. Enligt min åsikt är en av de viktigaste kriterierna vid val av SAST-verktyg möjligheten att anpassa det till egenskaperna hos sina applikationer, nämligen att skriva och ändra analysregler eller, som de oftare kallas, Custom Queries.

Hur man skriver regler för Checkmarx utan att bli galen

Vi använder oftast Checkmarx — en mycket intressant och kraftfull kodanalysator. I den här artikeln kommer jag att berätta om min erfarenhet av att skriva analysregler för det.

Innehållsförteckning

Inledning

För att börja skulle jag vilja rekommendera en av de få artiklarna på ryska om särdragen vid skrivandet av förfrågningar för Checkmarx. Den publicerades på Habr i slutet av 2019 under titeln: «Hello, Checkmarx!». Hur man skriver en förfrågan för Checkmarx SAST och hittar coola sårbarheter.

I den behandlas detaljerat hur man skriver sina första förfrågningar på CxQL (Checkmarx Query Language) för en viss testapplikation och de grundläggande principerna för hur analysregler fungerar.

Jag kommer inte att upprepa vad som beskrivs i den, även om vissa överlappningar ändå kommer att finnas. I min artikel kommer jag att försöka skapa en sorts “receptsamling”, en lista över lösningar för specifika uppgifter som jag har stött på under min tid med Checkmarx. För många av dessa uppgifter har jag fått tänka en hel del. Ibland har det saknats information i dokumentationen, och ibland har det varit svårt att förstå hur man ska göra det som krävs. Jag hoppas att min erfarenhet och sömnlösa nätter inte kommer att vara förgäves och att denna “samling av Custom Queries-recept” kommer att spara er några timmar eller ett par-tre nervceller. Låt oss börja!

Allmän information om regler

För att börja, låt oss titta på några grundläggande begrepp och processen bakom regelverket, för att bättre förstå vad som kommer att hända härnäst. Dessutom eftersom det inte nämns mycket om detta i dokumentationen eller är spritt över strukturen, vilket inte är så praktiskt.

  1. Reglerna tillämpas vid skanning beroende på den valda förinställningen (ett set av aktiva regler). Det går att skapa ett oändligt antal förinställningar och hur man strukturerar dem beror på karaktären av din process. De kan grupperas efter språk eller särskiljas för varje projekt. Antalet aktiva regler påverkar hastigheten och noggrannheten i skanningen.

    Hur man skriver regler för Checkmarx utan att bli galenInställning av förinställning i Checkmarx-gränssnittet

  2. Regler redigeras i ett specialverktyg som kallas CxAuditor. Detta är en skrivbordsapplikation som kopplar till servern med Checkmarx. Detta verktyg har två driftslägen: redigering av regler och analys av resultaten från tidigare genomförda skanningar.

    Hur man skriver regler för Checkmarx utan att bli galenCxAudit-gränssnittet

  3. Reglerna i Checkmarx är indelade efter språk, det vill säga för varje språk finns en egen uppsättning av frågor. Det finns också vissa allmänna regler som tillämpas oavsett språk, dessa kallas grundläggande frågor. I de flesta fall innehåller grundläggande frågor sökningar efter information som används av andra regler.

    Hur man skriver regler för Checkmarx utan att bli galenIndelning av regler efter språk

  4. Reglerna kan vara “Executable” och “Non-Executable” (Utförbara och Icke-Utörbara). Enligt min mening är det inte det mest korrekta namnet, men så är det. Poängen är att resultatet av att utföra “Executable”-regler visas i skanningsresultaten i användargränssnittet, medan “Non-Executable”-regler endast behövs för att använda deras resultat i andra frågor (i praktiken är det bara en funktion).

    Hur man skriver regler för Checkmarx utan att bli galenBestämning av regeltyp vid skapande

  5. Det går att skapa nya regler eller utöka/skriva om befintliga. För att skriva om en regel måste man hitta den i trädet, högerklicka och välja alternativet “Override” i rullgardinsmenyn. Det är viktigt att ha i åtanke att nya regler som standard inte ingår i förinställningarna och är inte aktiva. För att börja använda dem måste man aktivera dem i menyn “Preset Manager” i verktyget. Omskrivna regler behåller sina inställningar, det vill säga, om en regel varit aktiv förblir den aktiv och tillämpas omedelbart.

    Hur man skriver regler för Checkmarx utan att bli galenExempel på en ny regel i gränssnittet för Preset Manager

  6. Under körningen byggs ett "träd" av förfrågningar där varje förfrågan beror på den andra. Först utförs de regler som samlar information, och sedan de som använder den. Resultatet av körningen cachas, så om det finns möjlighet att använda resultaten från en befintlig regel är det bättre att göra det, vilket minskar skanningstiden.

  7. Regler kan tillämpas på olika nivåer:

  • För hela systemet — kommer att användas för varje skanning av vilket projekt som helst

  • På teamnivå (Team) — kommer att tillämpas endast för skanning av projekt inom det valda teamet.

  • På projektnivå — kommer att tillämpas i ett specifikt projekt

    Hur man skriver regler för Checkmarx utan att bli galenDefinition av nivån på vilken regeln kommer att tillämpas

“Ordbok” för nybörjare

Och jag börjar med några saker som har gett mig frågor, samt visar ett antal tekniker som väsentligt kommer att förenkla livet.

Operationer med listor

- subtraktion av ett från det andra (list2 - list1)
* skärning av listor (list1 * list2)
+ addition av listor (list1 + list2)

& (logiskt OCH) - förenar listor med matchning (list1 & list2), liknande skärning (list1 * list2)
| (logiskt ELLER) - förenar listor med bred sökning (list1 | list2)

Fungerar inte med listor: ^ && || % / 

Alla funna element

Inom det skannade språket kan man få en lista över alla element som Checkmarx har definierat (rader, funktioner, klasser, metoder osv.). Detta är ett visst rum av objekt som kan adresseras genom All. Det vill säga, för att söka efter ett objekt med ett specifikt namn searchMe, kan man göra en sökning, till exempel efter namnet i alla hittade objekt:

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

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

Men om man behöver göra en sökning på ett annat språk som av någon anledning inte ingick i skanningen (till exempel groovy i ett projekt för Android), kan man utöka vårt rum av objekt genom en variabel:

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

Funktioner för flowanalys

Dessa funktioner används i många regler och här är en liten guide till vad de betyder:

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

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

Hämta namn/stig till fil

Det finns flera attribut som kan hämtas från resultaten av förfrågan (filnamnet där förekomsten hittades, rad osv.), men hur man får dem och använder dem är inte angivet i dokumentationen. Så för att göra detta måste man referera till egenskapen LinePragma och inom den kommer de nödvändiga objekten att finnas:

// Для примера найдем все методы
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);

Värt att notera är att FileName innehåller egentligen sökvägen till filen, eftersom vi använde metoden GetFirstGraph.

Resultat av utförande

Inuti CxQL finns en speciell variabel result, som returnerar resultatet av din skrivna regel. Den är initialiserad omedelbart och kan användas för att skriva mellanresultat, ändra och förfina dem under arbetets gång. Men, om det inte finns någon tilldelning till denna variabel eller funktion inuti regeln återvända— kommer resultatet alltid att vara noll.

Den följande förfrågan kommer inte att returnera något som resultat och kommer alltid att vara tom:

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

Men, genom att tilldela resultatet av att köra den magiska variabeln result — ser vi vad detta anrop returnerar:

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

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

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

Användning av resultat från andra regler

Regler i Checkmarx kan kallas analoger till funktioner i ett vanligt programmeringsspråk. När du skriver en regel kan du helt enkelt använda resultaten från andra förfrågningar. Till exempel, det är inte nödvändigt att alltid söka efter alla metodanrop i koden, det räcker att anropa den nödvändiga regeln:

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

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

Denna metod gör att koden kan förkortas och avsevärt minska regeltidens utförande.

Lösning av problem

Loggning

Vid arbete med verktyget, ibland går det inte att direkt skriva den nödvändiga förfrågan och man måste experimentera, pröva olika alternativ. För sådana fall finns det loggning i verktyget, som åberopas på följande sätt:

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

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

Men det är viktigt att komma ihåg att den här metoden endast tar emot en sträng, så det går inte att visa hela listan över hittade element som resultat av den första operationen. Det andra alternativet som används för felsökning — är att ibland tilldela den magiska variabeln result resultatet av förfrågan och se vad som händer. Denna metod är inte särskilt bekväm, man måste vara säker på att det inte finns någon omdefinition eller operationer med detta i koden result eller enkelt kommentera de nedan liggande koden. Eller som jag, glömma att ta bort några av dessa anrop från den färdiga regeln och undra varför ingenting fungerar.

Ett mer bekvämt sätt — är att anropa metoden återvända med den nödvändiga parametern. I så fall kommer regeln att avslutas och vi kan se vad som hänt som resultat av det vi skrivit:

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

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

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

Problem med inloggning

Det finns situationer när det inte går att komma åt verktyget CxAudit (som används för att skriva regler). Det kan finnas många orsaker till detta, såsom oväntade programkrascher, plötsliga Windows-uppdateringar, BSOD och andra oförutsedda situationer som ligger utom vår kontroll. I sådana fall kan det ibland lämnas kvar en ofullständig session i databasen, vilket förhindrar att man loggar in igen. För att åtgärda detta måste man utföra flera förfrågningar:

För Checkmarx före 8.6:

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

För Checkmarx efter 8.6:

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

Skrivande av regler

Nu har vi kommit till det mest intressanta. När man börjar skriva regler i CxQL saknas ofta inte bara dokumentation, utan också levande exempel på hur man löser specifika uppgifter och en beskrivning av hur förfrågningarna fungerar i stort.

Jag ska försöka förenkla livet för dem som börjar dyka ner i språkformatet för förfrågningar och ge några exempel på hur man använder Custom Queries för att lösa specifika uppgifter. Vissa av dessa exempel är ganska generella och kan tillämpas i ditt företag nästan utan förändringar, medan andra är mer specifika, men de kan också användas genom att anpassa koden till dina applikationers behov.

Så här är några av de uppgifter vi oftast stött på:

Uppgift: I resultatet av regelförverkligandet finns flera Flow, och en av dem är en inbäddning av en annan; det är nödvändigt att behålla en av dem.

Lösningen: I själva verket visar Checkmarx ibland flera dataflöden som kan överlappa och vara en förkortad version av andra. För sådana fall finns det en särskild metod ReduceFlow. Beroende på parametern väljer den det kortaste eller längsta flödet:

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

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

Uppgift: Utvidga listan över känsliga data som verktyget reagerar på

Lösningen: I Checkmarx finns det grundläggande regler vars resultat används av många andra förfrågningar. Genom att komplettera några av dessa regler med data som är specifika för din applikation kan man omedelbart förbättra resultaten av skanningen. Här är ett exempel på en regel som man kan börja med:

General_privacy_violation_list

Låt oss lägga till några variabler som används i vår applikation för att lagra känslig information:

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

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

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

Uppgift: Utvidga listan över variabler med lösenord

Lösningen: Jag skulle rekommendera att omedelbart uppmärksamma den grundläggande regeln för att definiera lösenord i koden och lägga till en lista med variabelnamn som är vanliga inom ert företag.

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*"));

// Komplettera standardlistan för variabler
List  pswdIncludeList = new List{"*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  pswdExcludeList = new List{"*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);
	}
}

Uppgift: Lägg till de ramverk som inte stöds av Checkmarx

Lösningen: Alla förfrågningar i Checkmarx är uppdelade efter språk, så det är nödvändigt att komplettera reglerna för varje språk. Här är några exempel på sådana regler.

Om det används bibliotek som kompletterar eller ersätter standardfunktionaliteten kan de lätt läggas till i grundregeln. Då får alla som använder den omedelbart veta om nya tillägg. Till exempel bibliotek för loggning i Android — Timber och Loggi. I standarduppsättningen av reglerna för att identifiera icke-systemanrop finns det inget, så om ett lösenord eller sessionsidentifierare hamnar i loggen får vi ingen information om det. Låt oss försöka lägga till i Checkmarx-reglerna för att identifiera sådana metoder.

Ett testexempel på kod som använder biblioteket Timber för loggning:

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

Här är ett exempel på en begäran för Checkmarx som gör det möjligt att lägga till en definition av anrop av metoder Timber som en datautgångspunkt från applikationen:

FindAndroidOutputs

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

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

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

Det går också att komplettera den angränsande regeln som direkt hänför sig till loggning i Android:

FindAndroidLog_Outputs

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

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

Om det används i Android-appar WorkManager för asynkron bearbetning, är det bra att rapportera detta till Checkmarx genom att lägga till metoden för att hämta data från uppgiften getInputData:

FindAndroidRead

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

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

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

Uppgift: Sökning efter känsliga data i plist för iOS-projekt

Lösningen: Ofta används speciella filer med filändelsen .plist för att lagra olika variabler och värden i iOS. Att lagra lösenord, tokens, nycklar och andra känsliga data i dessa filer rekommenderas inte, eftersom de lätt kan extraheras från enheten.

Plist-filer har funktioner som inte är uppenbara för det blotta ögat men är viktiga för Checkmarx. Låt oss skriva en regel som kommer att söka efter de data vi behöver och informera oss om lösenord eller tokens nämns någonstans.

Ett exempel på en sådan fil där en token för kommunikation med backend-tjänsten är inbäddad:

DeviceDictionary
	
		phone
		iPhone 6s
		
	privatekey
		MIICXAIBAAKBgQCqGKukO1De7zhZj6+

Och en regel för Checkmarx som har flera nyanser att beakta vid skrivandet:

// Используем результат выполнения правила по поиску файлов 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);

Uppgift: Sökning av information i XML

Lösningen: Checkmarx har mycket praktiska funktioner för att arbeta med XML och söka efter värden, taggar, attribut och mer. Men i dokumentationen finns tyvärr ett fel som gör att inget exempel fungerar. Trots att detta fel har åtgärdats i den senaste versionen av dokumentationen – var försiktig om du använder tidigare versioner av dokumenten.

Här är ett felaktigt exempel från dokumentationen:

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

Som ett resultat av försöket att utföra kommer vi att få ett felmeddelande att vi All det finns ingen sådan metod... Och det stämmer, eftersom det finns ett specifikt, separat objektutrymme för att använda funktioner som arbetar med XML — cxXPath. Så här ser en korrekt förfrågan ut för att hitta inställningen i Android som tillåter användning av HTTP-trafik:

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

Vi går in lite mer i detalj, eftersom syntaxen för alla funktioner är liknande. När du har förstått en, behöver du bara välja den som passar. Så, i ordning utifrån parametrarna:

  • "*.xml"— filmasken som sökningen ska göras på

  • 8 — språkets id för vilket regeln tillämpas

  • "cleartextTrafficPermitted"— namnet på attributet i xml

  • "true" — värdet för detta attribut

  • false — användning av reguljära uttryck vid sökning

  • true — innebär att sökningen kommer att utföras utan att ta hänsyn till versaler, det vill säga case-insensitive

För exemplet används en regel som definierar felaktiga nätverksinställningar i Android ur säkerhetssynpunkt som tillåter kommunikation med servern via HTTP-protokollet. Exempel på inställningen som innehåller attributet cleartextTrafficPermitted med värdet true:

example.com
        
            
        
        
            secure.example.com

Uppgift: Begränsa resultaten efter namn\/filväg

Lösningen: I ett av de större projekten relaterade till utvecklingen av en mobilapplikation för Android stötte vi på falska identifieringar av en regel som definierar inställningen för obfuskering. Problemet är att regeln från början letar efter inställningen i filen build.gradle som ansvarar för tillämpningen av obfuskeringregler för applikationens versionsrelease.

Men i stora projekt förekommer ibland underfiler build.gradle, som hör till bibliotek som ingår i projektet. Det särskilda är att även om dessa filer inte anger behovet av obfuskering, kommer inställningarna från den överordnade byggfilen att tillämpas vid kompileringen.

Således ligger uppgiften i att stoppa identifieringar i underfiler som hör till bibliotek. Det går att identifiera dem genom att leta efter raden apply 'com.android.library'.

Exempel på kod från filen build.gradle, som definierar behovet av obfuskering:

tillämpa plugin: 'com.android.application'

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

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

dependencies {
  ...
}

Exempel på fil build.gradle för ett bibliotek som ingår i projektet och saknar sådan inställning:

tillämpa plugin: 'android-library'

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

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

Och regeln för 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);
		}
	}
}

Denna metod kan vara tillräckligt universell och användbar inte bara för Android-appar, utan även för andra tillfällen när det är nödvändigt att definiera en resultatfil.

Uppgift: Lägg till stöd för tredjepartsbibliotek om syntaxen inte stöds helt

Lösningen: Antalet olika ramverk som används vid kodning är överväldigande. Självklart känner inte Checkmarx alltid till deras existens och vår uppgift är att lära det att förstå att vissa metoder hänför sig till just detta ramverk. Ibland kompliceras detta av att ramverken använder funktionsnamn som är mycket vanliga och det är svårt att entydigt avgöra om ett visst anrop hör till ett specifikt bibliotek.

Svårigheten ligger i att syntaxen för sådana bibliotek inte alltid känner igen korrekt och vi måste experimentera för att inte få många falska positiva resultat. Det finns flera alternativ för att förbättra noggrannheten i scanningen och lösa problemet:

  • Det första alternativet, vi vet exakt att biblioteket används i ett visst projekt och kan tillämpa regeln på teamnivå. Men om teamet beslutar att använda en annan metod eller använder flera bibliotek där funktionsnamnen överlappar, kan vi få en obehaglig situation med många falska positiva resultat.

  • Det andra alternativet, tillämpa sökningar i filer där biblioteket tydligt importeras. Med denna metod kan vi vara säkra på att det i denna fil används det bibliotek vi behöver.

  • Och det tredje alternativet är att använda de två ovanstående metoderna tillsammans.

Som exempel kommer vi att analysera ett välbekant bibliotek inom begränsade kretsar slick för programmeringsspråket Scala, närmare bestämt funktionaliteten Splicing Literal Values. I allmänhet, för att överföra parametrar i en SQL-fråga måste man använda operatorn $, som sätter in data i en förhandlad SQL-fråga. Det är i praktiken en direkt motsvarighet till Prepared Statement i Java. Men i fall där man behöver dynamiskt konstruera en SQL-fråga, till exempel om man måste överföra tabellnamn, kan man använda operatorn #$, som direkt sätter in data i frågan (praktiskt taget som strängkonkatenering).

Exempel på kod:

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

Checkmarx kan för närvarande inte identifiera användningen av Splicing Literal Values och missar operatorerna #$, så vi ska försöka lära den att identifiera potentiella SQL-injektioner och markera nödvändiga ställen 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));
}

Uppgift: Sökning efter använda sårbara funktioner i Open-Source-bibliotek

Lösningen: Många företag använder verktyg för att kontrollera Open-Source (OSA-praxis), vilket gör det möjligt att upptäcka användningen av sårbara versioner av bibliotek i de applikationer som utvecklas. Ibland kan det vara omöjligt att uppdatera ett sådant bibliotek till en säker version. I vissa fall finns det funktionella begränsningar, i andra finns det helt enkelt ingen säker version. I sådana fall kan en kombination av SAST- och OSA-praxiser hjälpa till att avgöra att de funktioner som leder till utnyttjande av sårbarheter inte används i koden.

Men ibland, särskilt när det gäller JavaScript, kan det vara en ganska svår uppgift. Nedan presenteras en lösning, kanske inte perfekt, men ändå fungerande, med exempel på sårbarheter i komponenten lodash i metoderna template och *set.

Exempel på potentiellt sårbar testkod 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!'

Och vid direkt anslutning i html:

<!DOCTYPE html>
<html>
<head>
    <title>Lodash Handbok</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 letar efter alla våra sårbara metoder som anges i sårbarheterna:

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

Uppgift: Sökning efter hårdkodade certifikat i applikationen

Lösningen: Ofta använder applikationer, särskilt mobila, certifikat eller nycklar för att få åtkomst till olika servrar eller kontrollera SSL-Pinning. Sett ur säkerhetssynpunkt är det inte den bästa praktiken att lagra sådana saker i koden. Vi ska försöka skriva en regel som letar efter liknande filer i förvaret:

// Найдем все сертификаты по маске файла
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;

Uppgift: Sökning efter komprometterade tokens i applikationen

Lösningen: Det händer ofta att man behöver återkalla komprometterade tokens eller annan viktig information som finns i koden. Självklart är det inte den bästa idén att lagra dem i källkoden, men situationer kan vara olika. Tack vare CxQL-förfrågningar är det ganska enkelt att hitta sådana saker:

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

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

Slutsats

Jag hoppas att denna artikel kommer att vara till nytta för dem som börjar utforska verktyget Checkmarx. Kanske även de som har skrivit sina regler länge kommer att hitta något nyttigt i denna handbok.

Tyvärr saknas det just nu en resurs där man kan få nya idéer under utvecklingen av regler för Checkmarx. Därför har vi skapat ett repository på Github, där vi kommer att publicera våra resultat så att alla som använder CxQL kan hitta något användbart där, samt ha möjlighet att dela sina egna arbeten med gemenskapen. Repositoriesystemet är under innehållsbyggande och strukturering, så contributors are welcome!

Tack för uppmärksamheten!

Källa: habr.com

Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar 🔥 Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster