攻防世界reverse练习区WP

0x00. simple-unpack 使用exeinfope脱壳,拖进IDA shift+F12搜索字符串。 0x01. logmein 反编译得到如下代码: void __fastcall __noreturn main(int a1, char **a2, char **a3) { size_t v3; // rsi int i; // [rsp+3Ch] [rbp-54h] char s[36]; // [rsp+40h] [rbp-50h] BYREF int v6; // [rsp+64h] [rbp-2Ch] __int64 v7; // [rsp+68h] [rbp-28h] char v8[28]; // [rsp+70h] [rbp-20h] BYREF int v9; // [rsp+8Ch] [rbp-4h] v9 = 0; strcpy(v8, ":\"AL_RT^L*.?+6/46"); v7 = 0x65626D61726168LL; v6 = 7; printf("Welcome to the RC3 secure password guesser....

April 19, 2022 · zeroy

RSA算法详解

前置知识:欧拉定理 进阶知识:Miller Rabin算法(用于生成大素数),蒙哥马利算法(用于加快大数相乘再取模运算) 引入 历史上常规的密码加解密算法的流程如下: 假设甲要给乙发送数据,则需要甲通过一定的加密规则将数据加密,传送给乙,乙再通过一定的解密规则进行解密。 由于可靠性高的密码算法都要公开自己的加解密算法,因此,在数据传输的过程中,密钥的传输成了一个大难题。 因此RSA算法将密钥分为公钥和私钥两个部分,公钥任何人都可以获取到,而私钥只有数据接收方知道,接下来我们看看RSA加解密算法的运作流程。 算法流程 选定两个素数\(p\),\(q\),令\(n=pq,\phi{(n)}=(p-1)(q-1)\) 选取一个公钥\(e\),满足\(1<e<\phi{(n)}\),且\(e\)与\(\phi{(n)}\)互质 生成私钥 \(d\),满足\(ed\equiv1(mod\ \phi{(n)})\) 假设要发送的信息为\(m\),则加解密规则成立: $$ m^e\equiv c\pmod{n}\\ c^d\equiv m\pmod{n} $$ 可靠性分析 考虑甲向乙发送一串数据,乙只需要向甲传送\(n\)和\(e\),甲就可以将加密完成的\(c\)发还给乙,由乙来进行解密操作。 考虑第三方攻击者,只可能截获\(n\),\(e\),\(c\),若要获取私钥\(d\),则必须计算得\(n\)分解成的\(p\),\(q\)两数。 而大质数的因式分解所需要的运算量是非常恐怖的。因此,当选定的\(n\)很大时,RSA算法几乎不可能被破解。 总而言之:RSA利用的是,大数容易相乘,难以分解的特性,使得算法可靠。 代码实现 #RSA加解密算法实现 @copyright zeroy p=1000000007 q=998244353 n=p*q phi_n=(p-1)*(q-1) E=65537 def qkpow(a,b): ans=1 while b>0: if b%2==1: ans=ans*a%n a=a*a%n b=b//2 return ans def exgcd(a,b): if b==0: return 1,0,a else: x,y,q=exgcd(b,a%b) x,y=y,(x-(a//b)*y) return x,y,q # E**D=1(mod phi_n) def calcD(): x,y,q=exgcd(E,phi_n) return x+phi_n D=calcD() # C=m**E%n def encode(m): c=qkpow(m,E) return c # ans=c**D%n def decode(c): return qkpow(c,D) def main(): c=encode(234234) ans=decode(c) print(ans) main() 正确性证明 RSA算法过程以及正确性证明 - 简书 (jianshu....

March 31, 2022 · zeroy

[PWN.0x01]canary|partial overwrite|ret2libc

canary绕过 什么是canary? canary是一种防止栈溢出的保护机制,可以在终端中使用checksec命令检查ELF文件是否开启了canary保护。 是否开启canary的编译选项: gcc -o test test.c // 默认情况下,不开启Canary保护 gcc -fno-stack-protector -o test test.c //禁用栈保护 gcc -fstack-protector -o test test.c //启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码 gcc -fstack-protector-all -o test test.c //启用堆栈保护,为所有函数插入保护代码 在开启了canary的情况下,程序会在栈底额外存储一个值,并在函数return的时候检查这个值是否发生变动,从而判断程序是否发生了栈溢出,可以大大提高程序运行的安全性。 如何绕过canary? canary本身有个防意外输出的机制,由于printf %s函数以\x00为输出结束的标志,因此为了防止canary下面的合法数据段被合法写入的情况下,canary被printf函数意外输出,所以在小端序程序中,canary的最低位字节一定为\x00,因此可以进行partial overwrite,即部分覆盖。在写完合法的区域之后,溢出写入一个字节的数据把canary末尾的\x00覆盖掉,这样之后的printf函数就会顺带着泄露canary的值。 ret2libc 概述 在较为基础的pwn题中一般都有一个显性的system函数和/bin/sh字符串供选手调用。 如果这两者都不显性存在,就可以使用ret2libc方法。 这种方法主要针对**动态链接(dynamic linking)**的程序,程序运行时会调用 libc.so (程序被装载时,动态链接器会将程序所有所需的动态链接库加载至进程空间,libc.so 就是其中最基本的一个)。 libc.so 是 linux 下 C 语言库中的运行库glibc 的动态链接版,并且 libc.so 中包含了大量的可以利用的函数,包括 system() 、execve() 等系统级函数,我们可以通过找到这些函数在内存中的地址覆盖掉返回地址来获得当前进程的控制权。通常情况下,我们会选择执行 system("/bin/sh") 来打开 shell。 工作重心转向获取libc.so加载进内存的可利用的函数的地址。 什么是动态链接? 深入理解动态链接 - 简书 (jianshu.com) 深入理解GOT表和PLT表 - 知乎 (zhihu.com) got表和plt表在程序执行过程中的作用 - 云+社区 - 腾讯云 (tencent....

March 7, 2022 · zeroy

[PWN.0x00]函数调用栈结构与栈迁移

前置知识:汇编语言(第4版)前半本 注:本文适用于32位程序,示意图中上为高地址区,下为低地址区。若方框中字体为蓝色表示此处内存的地址,为黑色表示此处内存存储的值。示意图中leave均表示leave|ret 函数调用栈的基本结构 一些寄存器的作用: EIP:存储着下一条指令的地址,每执行一条指令,该寄存器变化一次。 EBP:存储着当前函数栈底的地址,通过将其与偏移地址相加减获取变量的地址。 ESP:始终指向栈顶。 在进入一个函数时,会执行如下操作。 push eip+4 push ebp mov ebp,esp 在执行完退出时,会执行如下操作。 mov esp,ebp pop ebp pop eip 即leave和ret。 整个过程描述起来就是,先将函数执行完成之后应当去执行的语句(eip+4)和主程序的的基址(ebp)压入栈中,然后再将ebp指向栈底。在函数执行完之后,再进行一遍上述过程的逆过程。 函数调用栈工作时的结构如下图: 其中val0地址处存储主程序传入的参数,如果有多个传入参数,它们将按照从右到左的顺序被push入栈中,val1~3为函数申请的局部变量。它们将按照申请的顺序被放入栈中。举例来讲,形如: void func(int a){ int b,c; char s[10]; } 这样的函数,a将被存入val0,b将被存入val1,c将被存入val2,s将被存入val3。 考虑最基础的栈溢出题目,由于程序将按照地址从低到高的顺序存储字符串变量,所以当s的数据由选手掌控且溢出空间足够大时,s将溢出到val2,val3,甚至旧ebp,eip的位置上。所以选手就可以控制旧ebp,eip处的内容,实现劫持程序返回,让程序执行恶意代码的目的。 但有时候,选手能掌控的数据量很有限,不足以支持我们完成过于复杂的劫持指令,对于这种情况,其中一种解题方法就是栈迁移。 栈迁移 栈迁移的核心思想是劫持当前函数已经压入栈中的eip段,将其内容改为一次leave,同时修改旧ebp段,借助旧ebp为跳板,实现对esp的控制,进而在下一次ret中,修改eip的值,实现目的。 也就是说,要构造栈中数据如图所示: 至于为什么要-4,后面模拟的过程中读者自能体会。 好了,现在当前函数已经执行完了,程序将自行执行一次leave和ret。执行之后的结果如图: 然后程序会执行eip指向的地址的命令,即再执行一次leave,ret,执行后栈的情况如图: 这样我们就完成了借助ebp为跳板,将esp指向目标eip的位置,控制程序在最后一次ret(即pop eip)的时候,将eip赋值成了我们想让它执行的函数的地址。 例题:ciscn_2019_s_4 BUUCTF在线评测 (buuoj.cn) 所谓“从零开始的Pwner生活”第一题,差点直接给我整劝退。 惯例checksec,只开了NX。 ida分析得到: int vul() { char buf[40]; // [esp+0h] [ebp-28h] BYREF memset(buf, 0, 0x20u); read(0, buf, 0x30u); printf("Hello, %s\n", buf); read(0, buf, 0x30u); return printf("Hello, %s\n", buf); } 发现只有8个字节的溢出空间,不足以支持一次完整的ROP,但是可以读两次,且第一次读入有输出,因此可以利用第一次读入来泄露ebp(注:这里的ebp指的是vul函数的ebp,即read函数栈帧中的旧ebp)。...

March 5, 2022 · zeroy