谷歌YARA-L 2.0——EDR hips、沙箱里、或者云端规则引擎都是可以用的
看yara的使用
https://go.chronicle.security/hubfs/YARA-L%20Overview%20White%20Paper.pdf
支持自定义函数:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | profile susp_process_with_variation_of_svchost { meta: author = “Chronicle Security” description = “Rule to detect process paths or command line execution of files with variations on svchost” version = “ 0.01 ” created = “ 2019 - 12 - 16 ”<br> function: func CheckSvchostVariations() if ( re.regex(strings.lower(udm.process.command_line), “. * (svch0st|svh0st|svhost|svchst|svchot|svchostexe)\.exe. * ”) or re.regex(strings.lower(udm.process.path), “. * (svch0st|svh0st|svhost|svchst|svchot|svchostexe)\.exe. * ”) ) then return true end return false end<br> condition: if ( CheckSvchostVariations() ) then outcome.match() end } |
可以用在EDR里,或者沙箱,都是可以的。检测ATT&CK:
https://github.com/chronicle/detection-rules/blob/main/mitre_attack/T1564_001_windows_hidden_files.yaral
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | rule mitre_attack_T1564_001_windows_hidden_files { meta: author = "Google Cloud Security" description = "Manually setting a file to be hidden on Windows" reference = "https://attack.mitre.org/techniques/T1564/001/" yara_version = "YL2.0" rule_version = "1.0" events: $e1.metadata.event_type = "PROCESS_LAUNCH" re.regex($e1.principal.process.command_line, `attrib\.exe \ + h`) nocase condition: $e1 } |
是采集进程信息,然后正则匹配。下面这个是采集注册表信息,然后匹配:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | rule mitre_attack_T1037_001_windows_logon_script { meta: author = "Google Cloud Security" description = "Registry modification related to installation of a custom logon script" reference = "https://attack.mitre.org/techniques/T1037/001/" yara_version = "YL2.0" rule_version = "1.0" events: ( $e1.metadata.event_type = "REGISTRY_CREATION" or $e1.metadata.event_type = "REGISTRY_MODIFICATION" ) and re.regex($e1.target.registry.registry_key, `(HKCU|HKEY_CURRENT_USER)\\Environment\\UserInitMprLogonScript`) nocase condition: $e1 } |
检测命令行的net use IPC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | rule mitre_attack_T1021_002_windows_admin_share { meta: author = "Google Cloud Security" description = "Net use commands for SMB/Windows admin shares" reference = "https://attack.mitre.org/techniques/T1021/002/" yara_version = "YL2.0" rule_version = "1.0" events: re.regex($e1.principal.process.command_line, `net use. * (C|ADMIN|IPC)$`) nocase condition: $e1 } |
创建计划任务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | rule T1053_005_windows_creation_of_scheduled_task { meta: author = "Google Cloud Security" description = "Creation of scheduled task using command line" reference = "https://attack.mitre.org/techniques/T1053/005/" yara_version = "YL2.0" rule_version = "1.0" events: re.regex($e1.principal.process.command_line, `schtasks / create`) nocase condition: $e1 } |
更多可以参考:https://github.com/chronicle/detection-rules/tree/main/mitre_attack,明显是不全哪。。。看来谷歌是有所保留的。
检测恶意软件的:
https://github.com/chronicle/detection-rules/blob/main/malware/apt_plugx_user_agent.yaral 从下面写的规则看,绝对是在乱搞,这样不知道有多少误报。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | rule malware_apt_plugx_user_agent { meta: author = "Google Cloud Security" description = "HTTP user-agent associated with PlugX" yara_version = "YL2.0" rule_version = "1.0" events: $e1.network.http.user_agent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1)" condition: $e1 } |
再看一个勒索的,这个签名要气死人。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | rule malware_ryuk_ransomnote_created { meta: author = "Google Cloud Security" description = "Identify when a Ryuk Ransomware ransomnote has been written" yara_version = "YL2.0" rule_version = "1.0" events: $e1.metadata.event_type = "FILE_CREATION" re.regex($e1.target. file .full_path, `ryukreadme\.(html|txt)$`) nocase condition: $e1 } |
再看可疑的行为:
https://github.com/chronicle/detection-rules/blob/main/suspicious/command_psexec.yaral
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | rule suspicious_command_psexec { meta: author = "Google Cloud Security" description = "Command-line execution of the PsExec tool on Windows" yara_version = "YL2.0" rule_version = "1.0" events: re.regex($e1.principal.process.command_line, `\bpsexec(\.exe)?\b`) nocase condition: $e1 } |
可疑的psexec、shutdown关机:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | rule suspicious_command_shutdown { meta: author = "Google Cloud Security" description = "Command-line execution of the 'shutdown' command" yara_version = "YL2.0" rule_version = "1.0" events: re.regex($e1.principal.process.command_line, `\bshutdown(\.exe)?\b`) nocase condition: $e1 } |
再看他狩猎的:
https://github.com/chronicle/detection-rules/blob/main/soc_prime_rules/threat_hunting/powershell/sysmon_service_enumeration.yaral
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | rule sysmon_service_enumeration { meta: author = "Osman Demir" description = "Detects Sysmon Enumeration Activity License: https://github.com/Neo23x0/sigma/blob/master/LICENSE.Detection.Rules.md." reference = "https://tdm.socprime.com/tdm/info/DnN9qhks24Tp" version = "0.01" created = "2021-03-09" product = "windows" service = "powershell" mitre = "discovery, t1063" events: (re.regex($selection.target.process.command_line, `ls HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Channels | Where - Object \\{$_\.name - like \ ".*sysmon.*\"\\}`) or $selection.target.process.command_line = " ls HKCU:\\Software\\Sysinternals | Select - Object name " or $selection.target.process.command_line = " Get - CimInstance win32_service - Filter \ "Description = 'System Monitor service'\"" ) condition: $selection } |
https://github.com/chronicle/detection-rules/blob/main/soc_prime_rules/threat_hunting/sysmon/abusing_azure_browser_sso.yaral
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | rule abusing_azure_browser_sso { meta: author = "Den Iuzvyk" description = "Detects abusing Azure Browser SSO by requesting OAuth 2.0 refresh tokens for an Azure-AD-authenticated Windows user (i.e. the machine is joined to Azure AD and a user logs in with their Azure AD account) wanting to perform SSO authentication in the browser. An attacker can use this to authenticate to Azure AD in a browser as that user. License: https://github.com/Neo23x0/sigma/blob/master/LICENSE.Detection.Rules.md." reference = "https://tdm.socprime.com/tdm/info/sX0F2FNTmN68" version = "0.01" created = "2021-03-09" category = "sysmon" product = "windows" mitre = "defense_evasion, privilege_escalation, t1073" events: (($selection_dll.metadata.product_event_type = "7" and re.regex($selection_dll.target.process. file .full_path, `. * MicrosoftAccountTokenProvider\.dll`)) and not ((re.regex($selection_dll.target.process. file .full_path, `. * BackgroundTaskHost\.exe`) or re.regex($selection_dll.target.process. file .full_path, `. * devenv\.exe`) or re.regex($selection_dll.target.process. file .full_path, `. * iexplore\.exe`) or re.regex($selection_dll.target.process. file .full_path, `. * MicrosoftEdge\.exe`)))) condition: $selection_dll } |
从官方规范文档看到的,
https://cloud.google.com/chronicle/docs/detection/yara-l-2-0-overview?hl=zh-cn
多事件关联的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 快速创建和删除用户 以下规则可搜索在 4 小时内创建然后删除的用户: rule UserCreationThenDeletion { meta: events: $create.target.user.userid = $user $create.metadata.event_type = "USER_CREATION" $delete.target.user.userid = $user $delete.metadata.event_type = "USER_DELETION" $create.metadata.event_timestamp.seconds <= $delete.metadata.event_timestamp.seconds match: $user over 4h condition: $create and $delete } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 事件变量:$create 和 $delete Match 变量:$user 占位符变量:不适用 下面介绍了此规则的工作原理: 对用户名为 ($user) 的事件进行分组,并在找到匹配项时返回该值 ($user)。 时间范围为 4 小时,这意味着只有不到 4 小时的事件才会关联。 搜索两个事件组($create 和 $delete,其中 $create 等同于 #create >= 1)。 $create 对应于 USER_CREATION 事件,并将用户 ID 调用为 $user。 $user 用于将两组事件联接在一起。 $delete 对应于 USER_DELETION 事件,并以 $user 形式调用用户 ID。此规则查找两个事件组中用户标识符相同的匹配项。 此规则查找事件 $delete 发生的时间晚于 $create 事件的情况,并在发现时返回匹配项。 |
此规则仅搜索用户的登录事件,然后返回在 Chronicle 帐号中存储的企业数据中首次遇到的事件。
下面是该规则的多事件版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | rule MultiEventRule { meta: author = "noone@altostrat.com" events: $e.metadata.event_type = "USER_LOGIN" $e.principal.user.userid = $user match: $user over 10m condition: #e >= 10 } |
该规则会搜索在 10 分钟内至少登录了 10 次的用户。
以下 YARA-L 2.0 滑动窗口示例搜索 firewall_1
事件后缺少 firewall_2
事件。after
关键字与数据透视事件变量 $e1
结合使用,指定在关联事件时应仅检查每个 firewall_1
事件 10 分钟后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | rule SlidingWindowRuleExample { meta: author = "noone@google.com" events: $e1.metadata.product_name = "firewall_1" $e1.principal.hostname = $host $e2.metadata.product_name = "firewall_2" $e2.principal.hostname = $host match: $host over 10m after $e1 condition: $e1 and !$e2 } |
其他:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | rule DetectionWithOutcomeSectionAndConditionalsAndAddition { meta: events: $u.udm.principal.hostname = $hostname $asset_context.graph.entity.hostname = $hostname $severity = $asset_context.graph.entity.asset.vulnerabilities.severity match: $hostname over 5m outcome: $risk_score = max( 100 + if ($hostname = "my-hostname" , 100, 50) + if ($severity = "HIGH" , 10) + if ($severity = "MEDIUM" , 5) + if ($severity = "LOW" , 1) ) condition: $u and $asset_context } |
YARA-L 2.0 语言概览
YARA-L 2.0 是一种计算机语言,用于创建搜索企业日志数据的规则,因为它被注入到您的 Chronicle 帐号中。YARA-L 语法派生自 VirusTotal 开发的 YARA 语言。该语言与 Chronicle Detection Engine 配合使用,让您可以在大量数据中寻找威胁和其他事件。另请参阅 YARA-L 2.0 语言语法
注意:YARA-L 2.0 与旧版 YARA-L 语言不兼容。以旧版 YARA-L 编写的规则不适用于当前版本的 Detection Engine,需要经过修改才能使用新语法。
规则结构
对于 YARA-L 2,您必须按以下顺序指定变量声明、定义和用法:
- meta
- events
- match(可选)
- condition
注意:如果您省略可选 match 部分,规则就可以与单个事件进行匹配。
下面说明了规则的通用结构:
rule <rule Name>
{
meta:
// Stores arbitrary key-value pairs of rule details, such as who wrote
// it, what it detects on, version control, etc.
// Identical to the meta section in YARA-L.
//
// For example:
// author = "Analyst #2112"
// date = "08/09/2020"
// description = "suspicious domain detected"
events:
// Conditions to filter events and the relationship between events.
match:
// Values to return when matches are found.
condition:
// Condition to check events and the variables used to find matches.
}
YARA-L 2.0 示例规则
以下示例展示了使用 YARA-L 2.0 编写的规则。每个示例都展示了如何关联规则语言中的事件。
不同城市的登录信息
以下规则会在 5 分钟内搜索从两个或多个城市登录过您的企业的用户:
rule DifferentCityLogin {
meta:
events:
$udm.metadata.event_type = "USER_LOGIN"
$udm.principal.user.userid = $user
$udm.principal.location.city = $city
match:
$user over 5m
condition:
#city > 1
}
Match 变量:$user
事件变量:$udm
Placeholder 变量:$city $user
下面介绍了此规则的工作原理:
- 对用户名为 (
$user
) 的事件进行分组,并在找到匹配项时返回该值 ($user
)。 - 时间范围为 5 分钟,表示只有间隔不到 5 分钟的事件相关联。
- 搜索事件类型为 USER_LOGIN 的事件组 (
$udm
)。 - 对于该事件组,该规则会将用户 ID 命名为
$user
,并将登录城市命名为$city.
- 如果 5 分钟时间范围内事件组 (
$udm
) 中的不同$city
值数量大于 1,则返回匹配项。
快速创建和删除用户
以下规则可搜索在 4 小时内创建然后删除的用户:
rule UserCreationThenDeletion {
meta:
events:
$create.target.user.userid = $user
$create.metadata.event_type = "USER_CREATION"
$delete.target.user.userid = $user
$delete.metadata.event_type = "USER_DELETION"
$create.metadata.event_timestamp.seconds <=
$delete.metadata.event_timestamp.seconds
match:
$user over 4h
condition:
$create and $delete
}
事件变量:$create
和 $delete
Match 变量:$user
占位符变量:不适用
下面介绍了此规则的工作原理:
- 对用户名为 (
$user
) 的事件进行分组,并在找到匹配项时返回该值 ($user
)。 - 时间范围为 4 小时,这意味着只有不到 4 小时的事件才会关联。
- 搜索两个事件组(
$create
和$delete
,其中$create
等同于#create >= 1
)。 $create
对应于USER_CREATION
事件,并将用户 ID 调用为$user
。$user
用于将两组事件联接在一起。$delete
对应于USER_DELETION
事件,并以$user
形式调用用户 ID。此规则查找两个事件组中用户标识符相同的匹配项。- 此规则查找事件
$delete
发生的时间晚于$create
事件的情况,并在发现时返回匹配项。
单事件与多事件
以下示例规则展示了如何创建用于搜索单个事件的规则,然后对其进行修改以搜索多个事件。
下面是该规则的单个事件版本:
rule SingleEventRule {
meta:
author = "noone@altostrat.com"
events:
$e.metadata.event_type = "USER_LOGIN"
condition:
$e
}
此规则仅搜索用户的登录事件,然后返回在 Chronicle 帐号中存储的企业数据中首次遇到的事件。
下面是该规则的多事件版本:
rule MultiEventRule {
meta:
author = "noone@altostrat.com"
events:
$e.metadata.event_type = "USER_LOGIN"
$e.principal.user.userid = $user
match:
$user over 10m
condition:
#e >= 10
}
该规则会搜索在 10 分钟内至少登录了 10 次的用户。
IP 地址范围内的单个事件
以下示例展示了一条规则,用于搜索两个特定用户和特定 IP 地址范围之间的匹配项:
rule OrsAndNetworkRange {
meta:
author = "noone@altostrat.com"
events:
// Checks CIDR ranges.
net.ip_in_range_cidr($e.principal.ip, "203.0.113.0/24")
// Detection when the hostname field matches either value using or.
$e.principal.hostname = /pbateman/ or $e.principal.hostname = /sspade/
condition:
$e
}
重复字段
在 YARA-L 2.0 中,重复字段表示为数组。
例如,主机可能有多个 IP 地址:
principal.ip [192.168.1.2, 10.3.4.100, 192.168.12.16]
或者一个电子邮件地址可能有多个收件人:
network.email.to ["a@google.com", "b@google.com", "c@google.com"]
Syntax
重复字段可以与非重复字段相同。在这种情况下,它们会自动消除歧义,这意味着系统会针对重复字段的各个元素检查条件。
例如,如果您在规则中包含以下语句:
$e.principal.ip = "192.168.12.16"
Chronicle 会在数组中搜索与 "192.168.12.16"
匹配的 IP 地址。在此示例中,将会找到匹配的地址并返回检测结果。
例如,如果您在规则中包含以下语句:
$e.principal.ip = "192.168.12.16" and $e.principal.ip = "10.3.4.100"
Chronicle 会在数组中搜索与 "192.168.12.16"
和 "10.3.4.100"
匹配的 IP 地址。在此示例中,将找不到匹配的地址,因此不会返回检测结果。
重复字段示例
以下规则会搜索来源 IP 地址已连接到目标 IP 地址的事件,同时在不到一分钟的时间内向 50 多个不同的目标端口发出请求。这可能是恶意实体搜索不安全的网络端口。
rule RepeatedFieldsRuleExample {
meta:
author = "noone@google.com"
events:
$e.principal.ip = $source_ip
$e.target.ip = $target_ip
$e.target.port = $target_port
match:
$source_ip, $target_ip over 1m
condition:
#target_port > 50
}
any 和 all 运算符
您还可以使用 any
和 all
运算符引用重复字段。如果是这样,它们不会消除歧义,也就是说,系统会针对重复字段的所有元素检查条件。
例如,如果您在规则中包含以下语句:
any $e.principal.ip = "192.168.12.16"
Chronicle 会检查数组中的任何 IP 地址是否与 "192.168.12.16"
匹配。在此示例中,数组将满足检查条件并返回检测结果。
如果您在规则中包含以下语句:
all $e.principal.ip = "192.168.12.16"
Chronicle 会检查数组中的所有 IP 地址是否与 "192.168.12.16"
匹配。在此示例中,数组不满足检查条件,因此不会返回检测结果。
如果您在规则中包含以下语句:
any $e.principal.ip = "192.168.12.16" and any $e.principal.ip = "10.3.4.100"
Chronicle 会检查数组中的任何 IP 地址是否与 "192.168.12.16"
匹配,以及数组中的任何 IP 地址是否与 "10.3.4.100"
匹配。在此示例中,数组会满足检查条件,并返回检测结果。
以下是使用 any
和 all
运算符的有效谓词示例:
any $e.principal.ip = "192.168.12.16"
net.ip_in_range_cidr(any $e.principal.ip, "192.168.12.16/24")
all $e.network.email.to = /.*@google\.com/
re.regex(all $e.network.email.to, `.*google\.com`)
以下是使用 any
和 all
运算符的无效谓词示例:
any $ip = "192.168.12.16"
any $e.principal.ip = all $e.target.ip
any $e.principal.ip in %reference_list
any 和 all 运算符的限制
any
和 all
运算符只能用于重复字段。此外,在将重复字段分配给 placeholder 变量或与其他事件的字段联接时,不能使用它们。
例如,any $e.principal.ip = $ip
和 any $e1.principal.ip = $e2.principal.ip
不是有效的语法。如需匹配或联接重复字段,请使用 $e.principal.ip = $ip
。重复字段的每个元素都有一个 match 变量值或联接。
使用 any
或 all
编写条件时,请注意使用 not
来排除条件的含义可能与使用否定运算符不同。
例如:
not all $e.principal.ip = "192.168.12.16"
会检查是否并非所有 IP 地址都与"192.168.12.16"
匹配,这意味着规则会检查是否有任何 IP 地址与"192.168.12.16"
不匹配。all $e.principal.ip != "192.168.12.16"
会检查是否所有 IP 地址都与"192.168.12.16"
不匹配,这意味着规则会检查是否没有 IP 地址与"192.168.12.16"
匹配。
any 和 all 规则示例
以下规则会搜索所有来源 IP 地址在 5 分钟的时间内与已知安全的 IP 地址不匹配的登录事件。
rule SuspiciousIPLogins {
meta:
author = "noone@google.com"
events:
$e.metadata.event_type = "USER_LOGIN"
// Detects if all source IP addresses in an event do not match "100.97.16.0"
// For example, if an event has source IP addresses
// ["100.97.16.1", "100.97.16.2", "100.97.16.3"],
// it will be detected since "100.97.16.1", "100.97.16.2",
// and "100.97.16.3" all do not match "100.97.16.0".
all $e.principal.ip != "100.97.16.0"
// Assigns placeholder variable $ip to the $e.principal.ip repeated field.
// There will be one detection per source IP address.
// For example, if an event has source IP addresses
// ["100.97.16.1", "100.97.16.2", "100.97.16.3"],
// there will be one detection per address.
$e.principal.ip = $ip
match:
$ip over 5m
condition:
$e
}
规则中的正则表达式
以下 YARA-L 2.0 正则表达式示例搜索从 altostrat.com 网域收到电子邮件的事件。由于 nocase
已添加到 $host
变量 regex
比较和 regex
函数,因此这些比较都不区分大小写。
rule RegexRuleExample {
meta:
author = "noone@altostrat.com"
events:
$e.principal.hostname = $host
$host = /.*HoSt.*/ nocase
re.regex($e.network.email.from, `.*altostrat\.com`) nocase
match:
$host over 10m
condition:
#e > 10
}
YARA-L 滑动窗口
默认情况下,YARA-L 2.0 规则使用跃点窗口进行评估。企业事件数据的时间范围分为一组重叠的跃点窗口,每个窗口的持续时间都在 match
部分指定。然后,在每个跃点窗口中关联事件。若有跃点窗口,则无法搜索按特定顺序发生的事件(例如,e1
可在 e2
后最多 2 分钟内发生)。只要事件 e1
与事件 e2
的跃点窗口时长相同,系统就会将它们相关联。
还可以使用滑动窗口评估规则。在滑动窗口时,以指定的数据透视事件变量开始或结束时,会生成时长为 match
区段的滑动窗口。然后,在每个滑动窗口内,事件是相关的。这样,您就可以搜索按特定顺序发生的事件(例如,e1
会在 e2
的 2 分钟内发生)。如果事件 e1
发生在事件 e2
后的滑动窗口内,则事件 e1
与事件 e2
的发生是相关的。
注意:已知使用滑动窗口代替跃点窗口会导致性能下降。Google 建议仅在特定情况下使用滑动窗口,例如绝对有必要使用事件顺序或搜索不存在的事件时。
滑动窗口规则语法
在规则的 match 部分中指定滑动窗口,如下所示:
<match-variable-name-1>
, <match-variable-name-2>
, ... over
<sliding-window- duration>
before|after
<pivot-event-variable-name>
数据透视事件变量是滑动窗口所基于的事件变量。如果使用 before
关键字,则系统会生成滑动窗口,以每次执行数据透视事件结尾。如果使用了 after
关键字,则系统会从每次发生透视事件开始生成滑动窗口。
注意:数据透视事件变量必须是有界限的。这意味着,condition
部分的事件变量必须至少存在一个边界条件。
滑动窗口规则示例
以下 YARA-L 2.0 滑动窗口示例搜索 firewall_1
事件后缺少 firewall_2
事件。after
关键字与数据透视事件变量 $e1
结合使用,指定在关联事件时应仅检查每个 firewall_1
事件 10 分钟后。
rule SlidingWindowRuleExample {
meta:
author = "noone@google.com"
events:
$e1.metadata.product_name = "firewall_1"
$e1.principal.hostname = $host
$e2.metadata.product_name = "firewall_2"
$e2.principal.hostname = $host
match:
$host over 10m after $e1
condition:
$e1 and !$e2
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
2018-03-30 leetcode 387. First Unique Character in a String
2018-03-30 leetcode 237. Delete Node in a Linked List
2018-03-30 leetcode 455. Assign Cookies
2018-03-30 python dig 模拟—— DGA域名判定用