1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
Sources: https://bugs.chromium.org/p/project-zero/issues/detail?id=796 https://bugs.chromium.org/p/project-zero/issues/detail?id=795 The usermode audio subsystem for the "Samsung Android Professional Audio" is based on JACK, which appears to be designed for single-user usage. The common JACK configuration on Linux systems appears to be a JACK server running under the current user account, and interacting with JACK clients from the same user account; so with a minimal privilege difference; this is not the case with the configuration on Android, where the JACK service runs as a more privileged user in a less restrictive SELinux domain to the clients that can connect to it. The shared memory implementation (implemented by com.samsung.android.IAndroidShm system service) allows any application to access/modify/map shared memory pages used by JACK, regardless of which application created those shared memory pages. (NB: This possibly results in breaking the Android permissions model and permitting applications without the required capability to access microphone input; this was not investigated further.) There are multiple possible ways to corrupt the internal state of any of the shared-memory backed c++ objects in use; attached is a PoC that uses the shared memory service to map the JackEngineControl object in use, and modify the value of the fDriverNum member, which is used in several places without validation. This is highly likely not the only variable stored in shared memory that is used without proper validation; and the function shown below is definitely not the only place that this particular variable is used dangerously. To secure this interface it will be necessary to review all uses of variables stored in these shared memory interfaces. /*! \brief Engine control in shared memory. */ PRE_PACKED_STRUCTURE struct SERVER_EXPORT JackEngineControl : public JackShmMem { // Shared state jack_nframes_t fBufferSize; jack_nframes_t fSampleRate; bool fSyncMode; bool fTemporary; jack_time_t fPeriodUsecs; jack_time_t fTimeOutUsecs; float fMaxDelayedUsecs; float fXrunDelayedUsecs; bool fTimeOut; bool fRealTime; bool fSavedRealTime;// RT state saved and restored during Freewheel mode int fServerPriority; int fClientPriority; int fMaxClientPriority; char fServerName[JACK_SERVER_NAME_SIZE+1]; JackTransportEngine fTransport; jack_timer_type_t fClockSource; int fDriverNum; bool fVerbose; // CPU Load jack_time_t fPrevCycleTime; jack_time_t fCurCycleTime; jack_time_t fSpareUsecs; jack_time_t fMaxUsecs; jack_time_t fRollingClientUsecs[JACK_ENGINE_ROLLING_COUNT]; unsigned int fRollingClientUsecsCnt; int fRollingClientUsecsIndex; int fRollingInterval; float fCPULoad; // For OSX thread UInt64 fPeriod; UInt64 fComputation; UInt64 fConstraint; // Timer JackFrameTimer fFrameTimer; #ifdef JACK_MONITOR JackEngineProfiling fProfiler; #endif ... This is quite a convenient exploitation primitive, as a small negative value will cause the code in several places to index backwards from a known array; when (any of the similar functions to the below are called, table is pointing to the fClientTable array inside a JackEngine instance) void JackTransportEngine::MakeAllLocating(JackClientInterface** table) { for (int i = GetEngineControl()->fDriverNum; i < CLIENT_NUM; i++) { JackClientInterface* client = table[i]; if (client) { JackClientControl* control = client->GetClientControl(); control->fTransportState = JackTransportStopped; control->fTransportSync = true; control->fTransportTimebase = true; jack_log("MakeAllLocating ref = %ld", i); } } } class SERVER_EXPORT JackEngine : public JackLockAble { friend class JackLockedEngine; private: JackGraphManager* fGraphManager; JackEngineControl* fEngineControl; char fSelfConnectMode; JackClientInterface* fClientTable[CLIENT_NUM]; We can see that just behind the fClientTable, we have two pointers to other objects; a JackEngineControl and a JackGraphManager, both of which are backed by shared memory. Since we are treating the pointer read from table as a c++ object with a vtable pointer, this lets us trivially gain control of the flow of execution. Fatal signal 11 (SIGSEGV), code 1, fault addr 0x41414140 in tid 27197 (jackd) *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Build fingerprint: 'samsung/zeroltexx/zerolte:6.0.1/MMB29K/G925FXXU3DPAD:user/release-keys' Revision: '10' ABI: 'arm' pid: 27181, tid: 27197, name: jackd>>> /system/bin/jackd <<< AM write failed: Broken pipe signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x41414140 r0 f3f1a000r1 f48c2010r2 f48c2010r3 41414141 r4 f3f1a000r5 00000036r6 f3dbf930r7 00000078 r8 f72c8b9cr9 f6f1a308sl f3d3f000fp f719a991 ip f71d7a0csp f3dbf7d8lr f7196c43pc 41414140cpsr 800f0030 backtrace: #00 pc 41414140<unknown> #01 pc 0003cc41/system/lib/libjackserver.so (Jack::JackTransportEngine::MakeAllLocating(Jack::JackClientInterface**)+52) #02 pc 0003cda1/system/lib/libjackserver.so (Jack::JackTransportEngine::CycleEnd(Jack::JackClientInterface**, unsigned int, unsigned int)+228) #03 pc 00048bd5/system/lib/libjackserver.so #04 pc 00049211/system/lib/libjackserver.so (Jack::JackEngine::Process(unsigned long long, unsigned long long)+228) #05 pc 000442fd/system/lib/libjackserver.so #06 pc 00044f49/system/lib/libjackserver.so (Jack::JackAudioDriver::ProcessGraphSyncMaster()+40) #07 pc 00044f0d/system/lib/libjackserver.so (Jack::JackAudioDriver::ProcessGraphSync()+20) #08 pc 00044e87/system/lib/libjackserver.so (Jack::JackAudioDriver::ProcessSync()+94) #09 pc 00044bbf/system/lib/libjackserver.so (Jack::JackAudioDriver::Process()+22) #10 pc 0004fff1/system/lib/libjackserver.so (Jack::JackThreadedDriver::Process()+24) #11 pc 0005051f/system/lib/libjackserver.so (Jack::JackThreadedDriver::Execute()+18) #12 pc 00040a0f/system/lib/libjackserver.so (Jack::JackAndroidThread::ThreadHandler(void*)+126) #13 pc 0003fc53/system/lib/libc.so (__pthread_start(void*)+30) #14 pc 0001a38b/system/lib/libc.so (__start_thread+6) Tombstone written to: /data/tombstones/tombstone_05 ################################################################################################################ The usermode audio subsystem for the "Samsung Android Professional Audio" is based on JACK, which appears to be designed for single-user usage. The common JACK configuration on Linux systems appears to be a JACK server running under the current user account, and interacting with JACK clients from the same user account; so with a minimal privilege difference; this is not the case with the configuration on Android, where the JACK service runs as a more privileged user in a less restrictive SELinux domain to the clients that can connect to it. The JACK shared memory implementation uses the struct jack_shm_info_t defined in /common/shm.h to do some bookkeeping PRE_PACKED_STRUCTURE struct _jack_shm_info { jack_shm_registry_index_t index; /* offset into the registry */ uint32_t size; #ifdef __ANDROID__ jack_shm_fd_t fd; #endif union { void *attached_at;/* address where attached */ char ptr_size[8]; } ptr;/* a "pointer" that has the same 8 bytes size when compling in 32 or 64 bits */ } POST_PACKED_STRUCTURE; typedef struct _jack_shm_info jack_shm_info_t; This struct is stored at the start of every JackShmAble object. /*! \brief A class which objects possibly want to be allocated in shared memory derives from this class. */ class JackShmMemAble { protected: jack_shm_info_t fInfo; public: void Init(); int GetShmIndex() { return fInfo.index; } char* GetShmAddress() { return (char*)fInfo.ptr.attached_at; } void LockMemory() { LockMemoryImp(this, fInfo.size); } void UnlockMemory() { UnlockMemoryImp(this, fInfo.size); } }; This means that whenever the JACK server creates an object backed by shared memory, it also stores a pointer to that object (in the address space of the JACK server), allowing a malicious client to bypass ASLR in the JACK server process. The PoC provided for the other reported JACK issue uses this to bypass ASLR in the JACK server process. Proof of Concept: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40066.zip |