l3akctf 中还是学到不少的内容,感觉国外的比赛会更偏向实战,一点一点去打国际比赛感觉也在慢慢的长进,虽然感觉自己还是有很多很多不足的地方需要自己去追赶,有一句话是这么说的,星光不负赶路人,时光不负有心人
之后会去复现一下自己当时 r3ctf 和 sekaictf 没有开出来的题目。
Rudimentary Calculator

题目直接给了.c 文件而不是二进制文件,不用反汇编的情况下直接分析源文件即可,
这里直接放源文件:
1 |
|
编译源文件是必要的,这里编译的时候开启了 canary 和 pie:

win
可以看到在 C 文件开头就有 win 函数可以直接去读 flag,相当于是一个后门

我们可以尝试去劫持一个地址到这里,然后就可以美美的拿到 flag
multiply_digit
multiply 乘法,该函数也就是一组乘法运算:
1 | void multiply_digit(int digit, uint32_t *product_bignum, int *product_bignum_len) { |
那么简单总结一下就是固定乘数,每次乘积后去掉低 32 位,只保留溢出的值为 carry,如果存在 carry,bignum 数组最后会多加一位,存放 carry
to_base_10
这个函数是将一个大整数以32位无符号整数数组的形式表示,转换为它的十进制字符串表示
1 | void to_base_10(uint32_t *product_bignum, int product_bignum_len, char *out) { |
run
run 函数当中有一个很明显的栈溢出,buf 有 0x1000 个字节的内存,但是 scanf 却可以读取无数个字节的内容。

规定了这么一个结构体,我们可以通过栈溢出去修改 product_bignum_len 的值
1 | struct { |
考虑到 canary 和 pie 保护,在缓冲区溢出的时候我们首先需要泄露 canary 和 pie
不过在 run 函数结尾,执行to_base_10后会打印 s.buf,如果我们可以通过上面说的栈溢出把s.product_bignum_len改大,我们就可以控制product_bignum继续往后读,把后面包含 canary 和 rbp 的地址都当做s.product_bignum转换成十进制,之后后面再逆向回来就可以拿到完整的 canary。
1 | to_base_10(s.product_bignum, s.product_bignum_len, s.buf); |
1 | from pwn import* |

修一下只保留数值:
1 | from pwn import* |

拿到数值之后,我们需要去取 canary,要把 10 进制逆向回 16 进制,还要注意 32 和 64 位之间的转换:
1 | from pwn import* |

既然找到了 canary 对应的下标,我们就可以知道 rbp 和返回地址对应的下标了。
1 | canary = limbs[97] | limbs[98] << 32 |
接下来我们泄露 pie:PIE = 真实返回地址减去二进制文件返回地址,在我们调用完 run 函数的时候由于 while 循环会反复调用,因此返回地址是 call run 之后的 0x1A9B

之后我们篡改返回地址为 win 即可:

EXP:
1 | from pwn import* |

后面还有两道题目,之后再进行补充


说些什么吧!