Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5e1e27c

Browse files
Adding queries related to the Solorigate campaign
1 parent 768e519 commit 5e1e27c

21 files changed

Lines changed: 911 additions & 0 deletions
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @name Potential dangerous use of native functions
3+
* @description Please review code for possible malicious intent or unsafe handling.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @precision low
7+
* @id cs/backdoor/dangerous-native-functions
8+
* @tags security
9+
* solorigate
10+
*/
11+
12+
import csharp
13+
import semmle.code.csharp.frameworks.system.runtime.InteropServices
14+
15+
predicate isDangerousMethod(Method m) {
16+
m.getName() = "OpenProcessToken" or
17+
m.getName() = "OpenThreadToken" or
18+
m.getName() = "DuplicateToken" or
19+
m.getName() = "DuplicateTokenEx" or
20+
m.getName().matches("LogonUser%") or
21+
m.getName().matches("WNetAddConnection%") or
22+
m.getName() = "DeviceIoControl" or
23+
m.getName().matches ("LoadLibrary%") or
24+
m.getName() = "GetProcAddress" or
25+
m.getName().matches ("CreateProcess%") or
26+
m.getName().matches ("InitiateSystemShutdown%") or
27+
m.getName() = "GetCurrentProcess" or
28+
m.getName() = "GetCurrentProcessToken" or
29+
m.getName() = "GetCurrentThreadToken" or
30+
m.getName() = "GetCurrentThreadEffectiveToken" or
31+
m.getName() = "OpenThreadToken" or
32+
m.getName() = "SetTokenInformation" or
33+
m.getName().matches ("LookupPrivilegeValue%") or
34+
m.getName() = "AdjustTokenPrivileges" or
35+
m.getName() = "SetProcessPrivilege" or
36+
m.getName() = "ImpersonateLoggedOnUser" or
37+
m.getName().matches ("Add%Ace%")
38+
}
39+
40+
predicate isExternMethod(Method externMethod) {
41+
externMethod.isExtern()
42+
or
43+
externMethod.getAnAttribute().getType() instanceof
44+
SystemRuntimeInteropServicesDllImportAttributeClass
45+
or
46+
externMethod.getDeclaringType().getAnAttribute().getType() instanceof
47+
SystemRuntimeInteropServicesComImportAttributeClass
48+
}
49+
50+
from MethodCall mc
51+
where isExternMethod(mc.getTarget())
52+
and isDangerousMethod(mc.getTarget())
53+
select mc, "Call to an external method $@", mc, mc.toString()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>This query finds native calls to external functions that are often used in creating backdoors or are generally attributed to unsafe code practices.</p>
7+
</overview>
8+
</qhelp>
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/**
2+
* @name Potential Timebomb
3+
* @description Flow from a file last modification date (very likely implant installation time) and an offset to condition statement (the trigger)
4+
* @kind problem
5+
* @precision Low
6+
* @problem.severity warning
7+
* @id cs/backdoor/potential-time-bomb
8+
* @tags security
9+
* solorigate
10+
*/
11+
12+
import csharp
13+
import DataFlow
14+
15+
/**
16+
* Class that will help to find the source for the trigger file-modification date.
17+
*
18+
* May be extended as new patterns for similar time bombs are found.
19+
*/
20+
class GetLastWriteTimeMethod extends Method {
21+
GetLastWriteTimeMethod() {
22+
this.getQualifiedName() in [ "System.IO.File.GetLastWriteTime", "System.IO.File.GetFileCreationTime", "System.IO.File.GetCreationTimeUtc", "System.IO.File.GetLastAccessTimeUtc" ]
23+
}
24+
}
25+
26+
/**
27+
* Abstracts `System.DateTime` structure
28+
*/
29+
class DateTimeStruct extends Struct {
30+
DateTimeStruct() {
31+
this.getQualifiedName() = "System.DateTime"
32+
}
33+
34+
/**
35+
* holds if the Callable is used for DateTime arithmetic operations
36+
*/
37+
Callable getATimeSpanArtithmeticCallable() {
38+
( result = this.getAnOperator() or result = this.getAMethod()) and
39+
( result.getName() in ["Add", "AddDays", "AddHours", "AddMilliseconds", "AddMinutes", "AddMonths", "AddSeconds", "AddTicks", "AddYears", "+", "-"] )
40+
}
41+
42+
/**
43+
* Holds if the Callable is used for DateTime comparision
44+
*/
45+
Callable getAComparisonCallable() {
46+
( result = this.getAnOperator() or result = this.getAMethod()) and
47+
( result.getName() in ["Compare", "CompareTo", "Equals", "==", "!=", "<", ">", "<=", ">="] )
48+
}
49+
}
50+
51+
/**
52+
* Dataflow configuration to find flow from a GetLastWriteTime source to a DateTime arithmetic operation
53+
*/
54+
private class FlowsFromGetLastWriteTimeConfigToTimeSpanArithmeticCallable extends TaintTracking::Configuration {
55+
FlowsFromGetLastWriteTimeConfigToTimeSpanArithmeticCallable() {
56+
this = "FlowsFromGetLastWriteTimeConfigToTimeSpanArithmeticCallable"
57+
}
58+
59+
override
60+
predicate isSource(DataFlow::Node source) {
61+
exists( Call call, GetLastWriteTimeMethod m |
62+
m.getACall() = call and
63+
source.asExpr() = call
64+
)
65+
}
66+
67+
override
68+
predicate isSink(DataFlow::Node sink) {
69+
exists( Call call, DateTimeStruct dateTime |
70+
call.getAChild*() = sink.asExpr() and
71+
call = dateTime.getATimeSpanArtithmeticCallable().getACall()
72+
)
73+
}
74+
}
75+
76+
/**
77+
* Dataflow configuration to find flow from a DateTime arithmetic operation to a DateTime comparison operation
78+
*/
79+
private class FlowsFromTimeSpanArithmeticToTimeComparisonCallable extends TaintTracking::Configuration {
80+
FlowsFromTimeSpanArithmeticToTimeComparisonCallable() {
81+
this = "FlowsFromTimeSpanArithmeticToTimeComparisonCallable"
82+
}
83+
84+
override
85+
predicate isSource(DataFlow::Node source) {
86+
exists( DateTimeStruct dateTime, Call call |
87+
source.asExpr() = call |
88+
call = dateTime.getATimeSpanArtithmeticCallable().getACall()
89+
)
90+
}
91+
92+
override
93+
predicate isSink(DataFlow::Node sink) {
94+
exists( Call call, DateTimeStruct dateTime |
95+
call.getAnArgument().getAChild*() = sink.asExpr() and
96+
call = dateTime.getAComparisonCallable().getACall()
97+
)
98+
99+
}
100+
}
101+
102+
/**
103+
* Dataflow configuration to find flow from a DateTime comparison operation to a Selection Statement (such as an If)
104+
*/
105+
private class FlowsFromTimeComparisonCallableToSelectionStatementCondition extends TaintTracking::Configuration {
106+
FlowsFromTimeComparisonCallableToSelectionStatementCondition() {
107+
this = "FlowsFromTimeComparisonCallableToSelectionStatementCondition"
108+
}
109+
110+
override
111+
predicate isSource(DataFlow::Node source) {
112+
exists( DateTimeStruct dateTime, Call call |
113+
source.asExpr() = call |
114+
call = dateTime.getAComparisonCallable().getACall()
115+
)
116+
}
117+
118+
override
119+
predicate isSink(DataFlow::Node sink) {
120+
exists( SelectionStmt sel |
121+
sel.getCondition().getAChild*() = sink.asExpr()
122+
)
123+
124+
}
125+
}
126+
127+
/**
128+
* Holds if the last file modification date from the call to getLastWriteTimeMethodCall will be used in a DateTime arithmetic operation timeArithmeticCall,
129+
* which is then used for a DateTime comparison timeComparisonCall and the result flows to a Selection statement which is likely a TimeBomb trigger
130+
*/
131+
predicate isPotentialTimeBomb( Call getLastWriteTimeMethodCall, Call timeArithmeticCall, Call timeComparisonCall, SelectionStmt selStatement ) {
132+
exists( FlowsFromGetLastWriteTimeConfigToTimeSpanArithmeticCallable config1, Node sink, DateTimeStruct dateTime,
133+
FlowsFromTimeSpanArithmeticToTimeComparisonCallable config2, Node sink2,
134+
FlowsFromTimeComparisonCallableToSelectionStatementCondition config3, Node sink3 |
135+
config1.hasFlow(exprNode(getLastWriteTimeMethodCall), sink) and
136+
timeArithmeticCall = dateTime.getATimeSpanArtithmeticCallable().getACall() and
137+
timeArithmeticCall.getAChild*() = sink.asExpr() and
138+
config2.hasFlow(exprNode(timeArithmeticCall), sink2) and
139+
timeComparisonCall = dateTime.getAComparisonCallable().getACall() and
140+
timeComparisonCall.getAnArgument().getAChild*() = sink2.asExpr() and
141+
config3.hasFlow(exprNode(timeComparisonCall), sink3) and
142+
selStatement.getCondition().getAChild*() = sink3.asExpr()
143+
)
144+
}
145+
146+
from Call getLastWriteTimeMethodCall, Call timeArithmeticCall, Call timeComparisonCall, SelectionStmt selStatement
147+
where isPotentialTimeBomb( getLastWriteTimeMethodCall, timeArithmeticCall, timeComparisonCall, selStatement )
148+
select selStatement, "Possible TimeBomb logic triggered by $@ that takes into account $@ from the $@ as part of the potential trigger."
149+
, timeComparisonCall, timeComparisonCall.toString()
150+
, timeArithmeticCall, "an offset"
151+
,getLastWriteTimeMethodCall, "last modification time of a file"
152+
153+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* @name ProcessName to hash function flow
3+
* @description Flow from a function retrieving process name to a hash function
4+
* @kind problem
5+
* @tags security
6+
* solorigate
7+
* @problem.severity warning
8+
* @precision medium
9+
* @id cs/backdoor/process-name-to-hash-function
10+
*/
11+
12+
import csharp
13+
import DataFlow
14+
import microsoft.code.csharp.Cryptography.NonCryptographicHashes
15+
16+
class DataFlowFromMethodToHash extends TaintTracking::Configuration {
17+
DataFlowFromMethodToHash() {
18+
this = "DataFlowFromMethodNameToHashFunction"
19+
}
20+
/**
21+
* Holds if `source` is a relevant data flow source.
22+
*/
23+
override predicate isSource(Node source)
24+
{
25+
isSuspiciousPropertyName(source.asExpr())
26+
}
27+
28+
/**
29+
* Holds if `sink` is a relevant data flow sink.
30+
*/
31+
override predicate isSink(Node sink)
32+
{
33+
isGetHash(sink.asExpr())
34+
}
35+
}
36+
37+
predicate isGetHash(Expr arg) {
38+
exists (MethodCall mc |
39+
(mc.getTarget().getName().matches ("%Hash%") or
40+
mc.getTarget().getName().regexpMatch("Md[4-5]|Sha[1-9]{1,3}")
41+
)
42+
and
43+
mc.getAnArgument() = arg) or
44+
exists( Callable callable, Parameter param, Call call, int i |
45+
isCallableAPotentialNonCryptographicHashFunction( callable, param ) and
46+
callable.getParameter(i) = param and
47+
call = callable.getACall() and
48+
call.getArgument(i) = arg)
49+
}
50+
51+
predicate isSuspiciousPropertyName(PropertyRead pr) {
52+
pr.getTarget().getQualifiedName() = "System.Diagnostics.Process.ProcessName"
53+
}
54+
55+
from Node src, Node sink, DataFlowFromMethodToHash conf
56+
where conf.hasFlow(src, sink)
57+
select src, "The hash is calculated on the process name $@, may be related to a backdoor. Please review the code for possible malicious intent.", sink, "here"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>This query detects code flow from PorcessName property on the Process class into a hash function.</p>
7+
<p>Such flow is often used in code backdoors to detect runnig processes and compare them to an obfuscated list of antivirus processes to aviod detection.</p>
8+
</overview>
9+
</qhelp>

0 commit comments

Comments
 (0)