✝️ Mark 2:15-17 And as he reclined at table in his house, many tax collectors and sinners were reclining with Jesus and his disciples, for there were many who followed him. And the scribes of the Pharisees, when they saw that he was eating with sinners and tax collectors, said to his disciples, “Why does he eat with tax collectors and sinners?” And when Jesus heard it, he said to them, “Those who are well have no need of a physician, but those who are sick. I came not to call the righteous, but sinners.” I recently learned that you can open the apple calendar on macos via **open ical://** and I wanted to learn more so I loaded the calendar app into ida pro and started by searching for the string "ical://", I found it was used in sub\_100094CE4 right here: ![[ida_01.png]] 64 bits at X20 + 0x20 being treated as an address is copied into X0, then the pseudo instruction ADRL is used to load the address of the string "ical://" into X2. X0 is checked to see if it starts with ical:// and the result is stored in X0, if bit 0 of W0, the lower 32 bits of X0, is 1 then the program counter is set to 0x100094DB8. If the TBNZ instruction didn't cause PC to be set to 0x100094DB8, then we do the same check but with the string "x-apple-calendarevent://". At 0x100094DB8, an instance of NSURL is made from the string at X20 + 0x28, if the result was NULL the PC is set to 0x100094E28, but if it was not NULL then we go here: ![[ida_02.png]] Now, X2 holds the new instance of NSURL. Now note that X19 is set equal to X2 and X25 is set equal to X0, some skippable stuff happens then we get here inside that method: ![[ida_03.png]] Now, here the scheme is turned into all lower case and checked if it equals "ical" and if it does not then PC is set to 0x1000A587C. If PC was not set to 0x1000A587C then this happens: ![[ida_04.png]] The address of the path is stored in X22, also we see that the query params "method" and "options" exist and the value for "method" is stored in X26 and the value for "options" is stored in X24. The address of the host is stored in X21 and is checked if it is not equal to "occurrence" and if so then PC is set to 0x1000A58B8. At 0x1000A58B8 we check if the host is equal to "ekevent" and if it is not equal PC is set to 0x1000A59E8. Now, we are going to see what happens if the host was equal to "occurrence" then we will see what happens if it was equal to "ekevent" or something else. Now we are here: ![[ida_05.png]] If the query value of "method" is equal to "show" and the length of the host is not zero, and the query value of "options" is either "more" or "less" or something else, if the query value is not more then the result of the comparison against "less" is stored in X27. Then we have this: ![[ida_06.png]] The query value of "view" is stored in X28 and X2 is set to X28 and X3 holds the result of the comparison if the query value of "options" was not more, after that handleViewParameter\_wantsToOpenInspector\_ is called. We will look at that in a bit. After that method returns the resource X28 holds is let go and X28 is set to a pointer that has the path without the first character. Now, showEventWithEventOccurrenceID\_OpenInspector\_ is called with X2 being set equal to X28 and X3 being equal to X27. After that method is called some cleanup stuff happens and the method returns. Now, lets look at what happens if the host is "ekevent". ![[ida_07.png]] So the query value of "view" is stored in X27 and handleViewParameter\_wantsToOpenInspector\_ is called with X2 being set equal to X27 and W3, the lower 32 bits of X3 being equal to 1, note that all of X3 is 1 because when you write to a W register it zero extends! The resource that X27 is freed. Now, I noticed something funny! **So, the code attempts to set X27 to a string equal to the path without the first character, the string is created from the substringFromIndex method called with the path, but what if the path is empty, also there is no length check for the path if the host was ekevent! =)** I ran the following command: `open_cal_if_dead() { if [[ $(pgrep Calendar) -eq 0 ]]; then echo dead; open /System/Applications/Calendar.app; return 0; else echo alive; return 1; fi }; open ical://ekevent/; sleep 5; open_cal_if_dead; sleep 5; open "ical://ekevent?"; sleep 5; open_cal_if_dead; sleep 5; open ical://ekevent; sleep 5; open_cal_if_dead; sleep 5; open ical://ekevent/1337; sleep 5; open_cal_if_dead; kill -9 $(pgrep Calendar)` and the output was: ``` alive dead dead alive ``` So it seems opening ical with the host being ekevent with no path results in calendar crashing. Anyways, the rest of the code if the host was ekevent isn't that interesting. Now, the other possible cases for host are date, nobo, showFamilyCalendar, and newFamilyCalendar. If the host is date, this happens: ![[ida_08.png]] If the host is nobo, then this happens: ![[ida_09.png]] If X0 is not zero at the end then PC goes here: ![[ida_10.png]] If host is showFamilyCalendar then this happens: ![[ida_11.png]] If host is newFamilyCalendar then this happens: ![[ida_12.png]] After all of that the function returns, now I said I would show yall what handleViewParameter\_wantsToOpenInspector\_ did and well I will tell you instead, it basically switches the calendar view based on the query value of "view" with the options being day, week, month, or year if whatever was in X2 was not 0 and if whatever was in X3 was 0. There are other options like if X2 was 0 and X3 was not zero. Soooo... uh click ->[HERE](ical://ekevent)<- if you want to crash calendar for macos!