Mailing List Archive

Inconsistent detection of tainted command in ${run} 4.96
Hi mailing list,

We have an external tool for checking user authentication, but running
it seems to be labelled as tainted inconsistently after upgrading to
4.96.

Here is our PLAIN auth:

plain:
driver = plaintext
public_name = PLAIN
server_prompts = :
server_condition = ${run,preexpand{/usr/bin/eximpassword
"--localpart=${local_part:$2}" "--hostname=${domain:$2}"
"--password=${rxquote:$3}"}{yes}{no}}
server_set_id = $2
server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}

This results in a failure to run as apparently the path is tainted even
though it is hard coded:

722716 plain authenticator server_condition:
722716 $auth1 =
722716 $auth2 = user@example.com
722716 $auth3 = Password!
722716 $1 =
722716 $2 = user@example.com
722716 $3 = Password!
722716 direct command:
722716 argv[0] = '/usr/bin/eximpassword'
722716 argv[1] = '--localpart=user'
722716 argv[2] = '--hostname=example.com'
722716 argv[3] = '--password=Password!'
722716 LOG: MAIN PANIC
722716 Attempt to exec tainted path: '/usr/bin/eximpassword'
722716 expansion failed: couldn't create child process: Operation not
permitted

However, and here's where things get confusing, here is our LOGIN auth
where we run an **identical** command:

login:
driver = plaintext
public_name = LOGIN
server_prompts = "Username:: : Password::"
server_condition = ${run,preexpand{/usr/bin/eximpassword
"--localpart=${local_part:$1}" "--hostname=${domain:$1}"
"--password=${rxquote:$2}"}{yes}{no}}
server_set_id = $1
server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}

This works, as if untainted:

723062 login authenticator server_condition:
723062 $auth1 = user@example.com
723062 $auth2 = Password!
723062 $1 = user@example.com
723062 $2 = Password!
723062 direct command:
723062 argv[0] = '/usr/bin/eximpassword'
723062 argv[1] = '--localpart=user'
723062 argv[2] = '--hostname=example.com'
723062 argv[3] = '--password=Password!'
723062 daemon-accept forking for expand-run
723062 daemon-accept forked for expand-run: 723068
723068 postfork: expand-run
723062 expanded string: yes
723062 SMTP>> 235 Authentication succeeded

The documentation on
https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html#SECID82
doesn't seem to cover how ${run} is checked for taint during expansion.

Why are seemingly identical commands inconsistently marked as tainted? Is
this a bug please? If it's by design is there any more documentation
about how ${run} is expanded please?

Thanks in advance,


--
Andrew.

--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-users.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-users-unsubscribe@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Inconsistent detection of tainted command in ${run} 4.96 [ In reply to ]
Hi mailing list,

We have an external tool for checking user authentication, but running
it seems to be labelled as tainted inconsistently after upgrading to
4.96.

Here is our PLAIN auth:

plain:
driver = plaintext
public_name = PLAIN
server_prompts = :
server_condition = ${run,preexpand{/usr/bin/eximpassword
"--localpart=${local_part:$2}" "--hostname=${domain:$2}"
"--password=${rxquote:$3}"}{yes}{no}}
server_set_id = $2
server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}

This results in a failure to run as apparently the path is tainted even
though it is hard coded:

722716 plain authenticator server_condition:

722716 $auth1 =

722716 $auth2 = user@example.com
722716 $auth3 = Password!
722716 $1 =

722716 $2 = user@example.com
722716 $3 = Password!
722716 direct command:

722716 argv[0] = '/usr/bin/eximpassword'

722716 argv[1] = '--localpart=user'
722716 argv[2] = '--hostname=example.com'
722716 argv[3] = '--password=Password!'
722716 LOG: MAIN PANIC

722716 Attempt to exec tainted path: '/usr/bin/eximpassword'

722716 expansion failed: couldn't create child process: Operation not
permitted

However, and here's where things get confusing, here is our LOGIN auth
where we run an *identical* command:

login:
driver = plaintext
public_name = LOGIN
server_prompts = "Username:: : Password::"
server_condition = ${run,preexpand{/usr/bin/eximpassword
"--localpart=${local_part:$1}" "--hostname=${domain:$1}"
"--password=${rxquote:$2}"}{yes}{no}}
server_set_id = $1
server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}

This works, as if untainted:

723062 login authenticator server_condition:
723062 $auth1 = user@example.com
723062 $auth2 = Password!
723062 $1 = user@example.com
723062 $2 = Password!
723062 direct command:
723062 argv[0] = '/usr/bin/eximpassword'
723062 argv[1] = '--localpart=user'
723062 argv[2] = '--hostname=example.com'
723062 argv[3] = '--password=Password!'
723062 daemon-accept forking for expand-run
723062 daemon-accept forked for expand-run: 723068
723068 postfork: expand-run
723062 expanded string: yes
723062 SMTP>> 235 Authentication succeeded

The documentation on
https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html#SECID82
doesn't seem to cover how ${run} is checked for taint during expansion.

Why are seemingly identical commands inconsistently marked as tainted?
Is this a bug please? If it's by design is there any more documentation
about how ${run} is expanded please?

Thanks in advance,


--
Andrew.

--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-users.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-users-unsubscribe@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: Inconsistent detection of tainted command in ${run} 4.96 [ In reply to ]
On Fri, 1 Sept 2023 at 10:50, Andrew Hearn via Exim-users
<exim-users@lists.exim.org> wrote:
>
> Hi mailing list,
>
> We have an external tool for checking user authentication, but running
> it seems to be labelled as tainted inconsistently after upgrading to
> 4.96.
>
> Here is our PLAIN auth:
>
> plain:
> driver = plaintext
> public_name = PLAIN
> server_prompts = :
> server_condition = ${run,preexpand{/usr/bin/eximpassword
> "--localpart=${local_part:$2}" "--hostname=${domain:$2}"
> "--password=${rxquote:$3}"}{yes}{no}}
> server_set_id = $2
> server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}
>
> This results in a failure to run as apparently the path is tainted even
> though it is hard coded:
>
> 722716 plain authenticator server_condition:
> 722716 $auth1 =
> 722716 $auth2 = user@example.com
> 722716 $auth3 = Password!
> 722716 $1 =
> 722716 $2 = user@example.com
> 722716 $3 = Password!
> 722716 direct command:
> 722716 argv[0] = '/usr/bin/eximpassword'
> 722716 argv[1] = '--localpart=user'
> 722716 argv[2] = '--hostname=example.com'
> 722716 argv[3] = '--password=Password!'
> 722716 LOG: MAIN PANIC
> 722716 Attempt to exec tainted path: '/usr/bin/eximpassword'
> 722716 expansion failed: couldn't create child process: Operation not
> permitted
>
> However, and here's where things get confusing, here is our LOGIN auth
> where we run an **identical** command:
>
> login:
> driver = plaintext
> public_name = LOGIN
> server_prompts = "Username:: : Password::"
> server_condition = ${run,preexpand{/usr/bin/eximpassword
> "--localpart=${local_part:$1}" "--hostname=${domain:$1}"
> "--password=${rxquote:$2}"}{yes}{no}}
> server_set_id = $1
> server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}}
>
> This works, as if untainted:
>
> 723062 login authenticator server_condition:
> 723062 $auth1 = user@example.com
> 723062 $auth2 = Password!
> 723062 $1 = user@example.com
> 723062 $2 = Password!
> 723062 direct command:
> 723062 argv[0] = '/usr/bin/eximpassword'
> 723062 argv[1] = '--localpart=user'
> 723062 argv[2] = '--hostname=example.com'
> 723062 argv[3] = '--password=Password!'
> 723062 daemon-accept forking for expand-run
> 723062 daemon-accept forked for expand-run: 723068
> 723068 postfork: expand-run
> 723062 expanded string: yes
> 723062 SMTP>> 235 Authentication succeeded
>
> The documentation on
> https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html#SECID82
> doesn't seem to cover how ${run} is checked for taint during expansion.
>
> Why are seemingly identical commands inconsistently marked as tainted? Is
> this a bug please? If it's by design is there any more documentation
> about how ${run} is expanded please?
>
> Thanks in advance,


Hello. I just recently upgraded to 4.96 and was hit with similar issues.

You need to get rid of the user set variables by doing lookups. I'm
not sure where you look them up elsewhere but well, I had:

server_condition = "${if
crypteq{$auth3}{${extract{1}{:}{${lookup{${local_part:$auth2}}lsearch{CONFDIR/domains/{domain:$auth2}/passwd}{$value}{*:*}}}}}{1}{0}}"

and Exim complained about the {domain:$auth2} so I had to replace it with:

server_condition = "${if
crypteq{$auth3}{${extract{1}{:}{${lookup{${local_part:$auth2}}lsearch{CONFDIR/domains/${lookup{${domain:$auth2}}dsearch{CONFDIR/domains}}/passwd}{$value}{*:*}}}}}{1}{0}}"

where I have directories for each domain under CONFDIR/domains, that
I'm matching the {domain:$auth2} against; if nothing's found the
lookup returns an empty string. This protects against injection
attacks.

Hope this sets you on the right path.

--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-users.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-users-unsubscribe@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: Inconsistent detection of tainted command in ${run} 4.96 [ In reply to ]
On 01/09/2023 08:48, Andrew Hearn via Exim-users wrote:
> If it's by design is there any more documentation
> about how ${run} is expanded please?

The docs I'm looking at,
https://exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html

say, for ${run...} :-

"If the option preexpand is used
[...]
Neither the command nor any argument may be tainted."

So, in your PLAIN authenticator you had used attacker-supplied data
($1) in the command args; with the preexpand option which requests the
entire command+args expanded as one string. The taint from $1 taints
the expanded string. That string is only then split for the argv
(executable path and arguments), and every element resulting is tainted.

You could track this getting done by using the "expand" debug channel
(probably there already, but you didn't show it to us).

What docs were you looking at?


Regarding your LOGIN authentication not behaving the same way, this
smells like a bug: failing to track taint in the more-complex
sequence that the LOGIN method uses. I'll look into this; thanks
for pointing it up.
--
Cheers,
Jeremy


--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-users.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-users-unsubscribe@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: Inconsistent detection of tainted command in ${run} 4.96 [ In reply to ]
On 01/09/2023 10:03, Jeremy Harris via Exim-users wrote:
> Regarding your LOGIN authentication not behaving the same way, this
> smells like a bug: failing to track taint in the more-complex
> sequence that the LOGIN method uses.

Addressed by 9b810c775c6e.
--
Cheers,
Jeremy


--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-users.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-users-unsubscribe@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/