攻防世界Reverse进阶区-SignIn-writeup

1. 介绍

本题是xctf攻防世界中Reverse的进阶区的题SignIn

题目来源: 2019_SUCTF

2. 分析

2.1 静态分析

$ file signin
signin: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=b98a398a7beb2d05a64c2679ad5b937c80ce533f, stripped
$ chmod +x signin
$ ./signin
[sign in]
[input your flag]: 123
GG!

扔到IDA里看一下。字符串[input your flag]: 在main函数中使用。用户输入保存在v8中。然后调用了sub_96A(&v8, &v9) ,参数是v8和v9。通过gdb调试可知,该函数将用户输入的十六进制保存在v9中。

再往下,调用了很多次 __gmpz_init_set_str 函数,通过网上的资料gmp.h 可以知道,这个函数其实是gmp.h里的mpz_init_set_str 函数。该函数的作用是把字符串初始化为gmp大整数,mpz_init_set_str 函数的三个参数分别是多精度整数变量,字符串,进制。

gmp又叫GNU多精度算术库,是一个提供了很多操作高精度的大整数,浮点数的运算的算术库,几乎没有什么精度方面的限制,功能丰富。GMP的主要目标应用领域是密码学的应用和研究、 互联网安全应用、 代数系统、 计算代数研究等。

#define mpz_init_set_str __gmpz_init_set_str
__GMP_DECLSPEC int mpz_init_set_str (mpz_ptr, const char *, int);

说白了,mpz_init_set_str(mpz_ptr, const char *, int) 函数,就是根据第三个参数指定的进制,对第二个参数字符串进行转换,并保存到第一个参数中。

比如,mpz_init_set_str(z_i, "1", 10); 就是将1看成十进制的数,保存到z_i中,运行完该函数以后,z_i的值为1。

再比如,题目中的

__gmpz_init_set_str((__int64)&v7, (__int64)"ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL);  

就是将字符串"ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35" 看做是一个十六进制数,将其保存到v7中,v7中的值为数字类型的十六进制数ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35

这里,可以写个脚本测试一下:

#include <gmp.h>
#include <stdio.h>

int main(void){
        mpz_t v7;
        mpz_init_set_str(v7, "ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16);
        gmp_printf("%Zx\n", v7);

        mpz_t pie;
        mpz_init_set_str (pie,"3141592653589793238462643383279502884", 10);
        gmp_printf("%Zd\n", pie);

        return 0;
}

编译:

gcc -o test test.c -lgmp -lm

运行,输出:

$ ./test
ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
3141592653589793238462643383279502884

用python验证一下:

v7_raw = "ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35"
v7 = "78510953323073667749065685964447569045476327122134491251061064910992472210485"
str(int(v7_raw, 16)) == v7  # True

因此,main函数中的这段代码,就是以不同的进制,将字符串转为数字。
其中,用户输入的字符串先转成16进制字符串v9,再通过__gmpz_init_set_str函数变成十六进制数v6。

在这里插入图片描述

然后,调用了__gmpz_powm 函数。根据文档gmp_lib.mpz_powm Method ,函数的原型为:

在这里插入图片描述

也就是:rop = (base ^ exp) % mod,求base的exp次方,再对mod取模以后的结果保存到rop中。对应到本题中,就是v6 = (v6 ^ v5) % v4 。最终,v6必须与v7相等才行,即 v7 = (v6 ^ v5) % v4,求v6。

本来想写个脚本跑一下,但是好像不是很好写嘛…

2.2 寻找网上的writeup

实在想不出来咋写,去网上找了下writeup:复盘 SUCTF 2019 Reverse 精解

文章里提到本题其实是一个已知密文,求解RSA明文的题,需要用到yafu 这个工具。

关于RSA的介绍,可以参考阮一峰老师的博客:RSA算法原理(二)

看来还是自己的知识面太窄了…

D:\sectools\yafu-1.34>yafu-x64.exe
factor(103461035900816914121390101299049044413950405173712170434161686539878160984549)


fac: factoring 103461035900816914121390101299049044413950405173712170434161686539878160984549
fac: using pretesting plan: normal
fac: no tune info: using qs/gnfs crossover of 95 digits
div: primes less than 10000
fmt: 1000000 iterations
rho: x^2 + 3, starting 1000 iterations on C78
rho: x^2 + 2, starting 1000 iterations on C78
rho: x^2 + 1, starting 1000 iterations on C78
pm1: starting B1 = 150K, B2 = gmp-ecm default on C78
ecm: 30/30 curves on C78, B1=2K, B2=gmp-ecm default
ecm: 74/74 curves on C78, B1=11K, B2=gmp-ecm default
ecm: 161/161 curves on C78, B1=50K, B2=gmp-ecm default, ETA: 0 sec

starting SIQS on c78: 103461035900816914121390101299049044413950405173712170434161686539878160984549

==== sieving in progress (1 thread):   36224 relations needed ====
====           Press ctrl-c to abort and save state           ====
36211 rels found: 18754 full + 17457 from 185306 partial, (2471.78 rels/sec)

SIQS elapsed time = 84.1098 seconds.
Total factoring time = 100.7290 seconds


***factors found***

P39 = 366669102002966856876605669837014229419
P39 = 282164587459512124844245113950593348271

ans = 1

因此,根据阮一峰老师的博客,

p = 366669102002966856876605669837014229419 
q = 282164587459512124844245113950593348271

e = 65537

接下来就可以求出私钥 d,并通过私钥 d,求出明文 m,再将其转化成 ASCII 即可得到 flag:suctf{Pwn_@_hundred_years}

gmpy2的安装可以参考Ubuntu 安装gmpy2模块


import gmpy2 
p = 366669102002966856876605669837014229419 
q = 282164587459512124844245113950593348271 
N = 103461035900816914121390101299049044413950405173712170434161686539878160984549 
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35 
e = 65537 
d = gmpy2.invert(e,(p-1)*(q-1)) 
m = gmpy2.powmod(c,d,p*q) 

print(ex(m)[2:].decode('hex'))

3. 总结

平时应该对一些通信的过程、加密算法有一些了解。还有对一些解密的工具也有些了解。可以适当做点Crypto的题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值