anti-csrf tokens detection plugin

968 views
Skip to first unread message

Guifre Ruiz Utges

unread,
Sep 10, 2012, 8:12:22 AM9/10/12
to zaproxy...@googlegroups.com
Hello,

A few weeks ago a ZAP user got in touch in the users group to complain about some CSRF false positive alerts in ZAP.  https://groups.google.com/forum/?fromgroups=#!topic/zaproxy-users/8BJZiH816bw

I thought we could try to dynamically find anti-csrf tokens in the targeted site by analyzing their behavior instead of having a list of known tag names.

I have been a few days working on it and this is the algorithm I thought we could use to detect these tokens:

For each page I store the hidden input tag elements of the html forms. I request again the page without appending the cookies header. Therefore, a new session is created and the anti-csrf tokens should change. If one of the hidden input fields have a different value, then it is an anti-csrf token.

It could also perform a request in the beginning to check that the hidden input value did not change when using the same session to avoid some potential false positives.

By using this algorithm we could detect anti-csrf tokens not matter if they are cookie or session based.

Does this makes sense? Do you think it can be an interesting feature? Any suggestion will be gratefully appreciated.

Many thanks and best regards.

Colm O'Flaherty

unread,
Sep 10, 2012, 9:13:06 AM9/10/12
to zaproxy...@googlegroups.com
Hi Guifre.

I saw the same thing a few months ago, and it was for that reason that I re-packaged the existing "CSRF" scanner code as the correctly named "CSRF countermeasures" scanner (as a plugin in the extensions project).  The only thing that was missing to remove the false positives was to disable the original CSRF scanner.  This is still on my list to do for 2.0, if I can get convince Simon that it is the right thing to do :)

Like you, I prefer to "discover" various tokens, rather than simply relying on a given list, which can never be complete.  Admittedly, it's a slower way to do it, but it's more generic, and is better guaranteed to work on an arbitrary web page, especially given a user inexperienced with ZAP (who won't check/modify the list of tokens in the options page).  I used this technique with the Session Fixation scanner: it determines the session tokens (cookie based/form field based/url re-writing) based purely on their behaviour. 

In addition, one of the side effects of the session fixation scanner is that it typically identifies the anti-CSRF tokens for you, since the anti-CSRF tokens and session tokens have a lot in common.  I suggest you have a look here first, before writing any new code, since this existing logic may save you some work.

Don't forget that anti-CSRF tokens could use any of the following implementations (and maybe others?):
- cookie based
- URL rewriting based
- Form field based
- query parameter based

It would be great to have some improved CRSF token detection, so I'd support you.

+1 from me.

Colm

--
 
 

psiinon

unread,
Sep 10, 2012, 9:19:12 AM9/10/12
to zaproxy...@googlegroups.com, colm.p.o...@gmail.com
+1 from me too :)

When we first introduce features I think we should aim at simple and effective, with the user in control.
Which is where the anti csrf extension is right now.
But it would be great to improve this and to detect such tokens automatically.
I think this should be optional, but depending on how effective it is on real apps it could well be on by default.

Cheers,

Simon

Guifre Ruiz Utges

unread,
Sep 13, 2012, 1:36:07 PM9/13/12
to zaproxy...@googlegroups.com, colm.p.o...@gmail.com
Hello,

I created the first prototype aimed at detecting anti-csrf countermeasures based on the parameters behavior.

I made a new plugin in zap-exts as I did not want to overwrite the current one (but it is highly based on its code). It can be found at https://code.google.com/p/zap-extensions/source/detail?r=178

I tested it in 3 different applications and I did not get neither false positives nor false negatives. However, it should be tested in more environments. I have not been able to test query based ones but all cookie and form field ones should work fine.

There is a difference in the detection criteria I took: If there isn't any user-controllable parameter, the page will never be vulnerable to CSRF attacks. CSRF attacks involve performing some critical action on behalf of the victims. Therefore, if there isn't any <form> in the targeted page, there isn't any action to exploit. Does this make sense?

The plugin only performs one HTTP request per page that has <form> tags, so its quite fast.

I don't know If you think this is the way to go. Another potential approach I thought about was to create a new HTTP request with a new session and to determine if the actions have been carried out after it, or otherwise an error has been thrown.

Does someone know a webapp with anti-csrf features I can deploy to test it against?

Best regards.
--
Guifre

Colm O'Flaherty

unread,
Sep 13, 2012, 4:11:43 PM9/13/12
to Guifre Ruiz Utges, zaproxy...@googlegroups.com
"Therefore, if there isn't any <form> in the targeted page, there isn't any action to exploit"

In theory, and in a well written application, I would tend to agree, but in general, it's not strictly true.  It's possible to write an application with a page with *no form* that performs actions on behalf of the user.  For example:

http://www.insecure-bank.com/transfer-cash.php?from=12345678&to=98765432&anticsrftokenid=991992929929292

The url performs an action on behalf of the user, but there is no form (there could happen to be, but it isn't necessary).  The example anti-csrf implementation there is in the form of an additional url parameter, but could be in a cookie, url re-write parameter, or whatever.

When the request is submitted, the server side should check that the anticsrf token value was given to the session which made the request (as given by a cookie, sessionid, form param or whatever)..  In addition.. if there was a Session Fixation vulnerability, the attacker could completely sidestep a working anti-csrf implementation, by first setting the victim's session id to one under the attacker's control!

Apparently Drupal.org 4.7.4+ builds in an anti csrd token into forms.. so if you could find a sample Drupal app with some forms, you could use that for the "form" test cases.

Colm

psiinon

unread,
Sep 17, 2012, 4:57:31 AM9/17/12
to zaproxy...@googlegroups.com, colm.p.o...@gmail.com
Not had a chance to try this yet, but had a quick look at the code.

It looks like you are clearing all of the cookies before making the second request.
If so that will be a problem if the form is only accessible to authenticated users.
Thats because you'll loose the session cookie and so the second request will probably get redirected to the login page.
A way round this would be not to clear potential session cookies - you can get details about all of the parameters from the params extension, although it would be better if this extension provided simple methods like 'isSessionParameter(paramName)' ;)

Cheers,

Simon

Guifre

unread,
Sep 17, 2012, 9:04:11 AM9/17/12
to zaproxy...@googlegroups.com, colm.p.o...@gmail.com
Hello,

Thanks for the comments.

@Colm, I meant <input> tags, which represent user parameters in get or
post requests, instead of <form> ones.

@Simon, Usually, it is used the same anti-csrf token during a given
web server session. The idea of starting a new session was to check
what input hidden fields changed in the targeted page. Those would be
detected as anti-csrf tokens.

If the page can not be requested with a new session, I will need to
think about another approach such as making the request without
appending the potential anti-csrf input fields and trying to determine
whether the action was carried out or not but that seems both less
simple and deterministic. Any ideas?

I tested the plugin against a Drupal box and it detected the anti-csrf
tokens properly.

Best regards.
--
Guifre.

Colm O'Flaherty

unread,
Sep 17, 2012, 9:49:38 AM9/17/12
to Guifre, zaproxy...@googlegroups.com
Regarding <input>/<form> tags: regardless of which form field the scanner checks for, if the page does not have a form, but instead uses a URL parameter based anti-csrf impleementation, the scanner will not be aware of it.  That's not necessarily an issue for v1 of the scanner. I just wanted you to be aware that an anti-csrf implementation does not need to be form/input based.

To clarify things, can you give us two actual scenarios, with and without anti-CSRF countermeasures, with details of how you check for it?  This would help make the discussion more concrete.

Thanks,
Colm

Guifre Ruiz Utges

unread,
Sep 17, 2012, 11:08:57 AM9/17/12
to zaproxy...@googlegroups.com, Guifre, colm.p.o...@gmail.com
Hello,


The current scanner algorithm works as follows:

For each page:

1. First, it checks whether the page has input parameters (user-controllable data) or not. If so, the page potentially performs a critical action, and therefore, it can potentially be vulnerable to CSRF attacks. Otherwise, the page does not perform security critical actions and performing a CSRF attack does not produce any impact. If you disagree with this statement, we could have this step replaced by "everything vulnerable except if it has an anti-csrf countermeasure" as, AFAIK, the current plugin does.


For the potential vulnerable pages, the scanner will try to guess if there is a anti-csrf token or not as follows:

2. The scanner creates a hash map of the input hidden fields of the targeted page.

3. The scanner starts a new session, requests again the page and matches the new input hidden fields with the previous ones.

4. If there is a input hidden field whose value changed in the second request, it will be determined as an anti-csrf token and the page will not be marked as vulnerable to CSRF attacks. Otherwise, it will.


I tested it in pages that have built-in anti-csrf parameters and the plugin has successfully identified those when the current one couldn't:

72206 [Thread-333] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The page has parameters, marked temporary vulnerable
372221 [Thread-333] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: utf8, ✓
372221 [Thread-333] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: authenticity_token, +jMbvQqYQOhtAlStdMZAjccj2e4h/tkLkiARkkhvG+U=
372221 [Thread-333] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: owner, null
372221 [Thread-333] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: authenticity_token, +jMbvQqYQOhtAlStdMZAjccj2e4h/tkLkiARkkhvG+U=
372221 [Thread-333] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: user_facebook_uid, 
372221 [Thread-333] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: user_facebook_access_token, 
372221 [Thread-333] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: user_name, 
372221 [Thread-333] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: show_facebook_status, true
373016 [Thread-333] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Found Anti-CSRF token: authenticity_token, 9lGHYOLNEOXsEr9ObQ1SYH0RBrb4go75N0hFuPGNgLg=

An example of a vulnerable page:

557423 [Thread-343] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The page has parameters marked temporary vulnerable
557432 [Thread-343] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Page vulnerable to CSRF attacks, the input hidden field did not change in the new session.
557432 [Thread-343] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - New alert pluginid=20012 None. Warning only. uri=http://aopcgr.uab.es:10001/wivet/pages/6.php

I hope this helps.

Best Regards,
Guifre

Colm O'Flaherty

unread,
Sep 17, 2012, 12:22:23 PM9/17/12
to Guifre Ruiz Utges, zaproxy...@googlegroups.com
A few more points:

- if the hidden form field that you identify is actually the session id field (rather than an anti-csrf field), in a hidden form field based session implementation, where the session id changes for *every request*, then this logic will raise a false alert on it as being vulnerable to CSRF (false positive).
- assume a form that does *not* have any anti-csrf implementation.  if there is a single hidden form field that changes with each request (a counter, for instance), the logic will not raise an alert (false negative).

I'd be more worried about the first scenario than the second scenario, which although possible, is far less likely to occur.

Rather than saying that "everything" else is vulnerable, I would prefer to simply say: "this is version 1, which only looks at form based anti-csrf implementations.  Version 2 might look at non-form-based anti-csrf implementations such as url parameters, url rewriting, and cookies", or something along those lines.  Simon or others might disagree with me on this, of course :)

The whole anti-CSRF thing has not been done well in the past because it's inherently tricky, IMHO.

Fa moltes anys, vaig estudiar en UAB tambe!

Colm

Vitor Meireles

unread,
Sep 17, 2012, 12:48:11 PM9/17/12
to zaproxy...@googlegroups.com, Guifre Ruiz Utges
On the points you highlighted Colm, what should be raised here is information leakage/sensitive information leakage since having a valid session id on the browser's history can potentially allow an attacker to have a valid access into the application. But this is more on other scanners that this one I agree (and I think it's actually on one of my scanners that I'm doing on the portage of Watcher scanner rules into ZAP - which I didn't had the time to get on it since 2 months, my fault).

Cheers,

Vitor Meireles De Sousa

email: desous...@gmail.com
mobile: +41 (0)76 349 80 92


2012/9/17 Colm O'Flaherty <colm.p.o...@gmail.com>
--
 
 

Colm O'Flaherty

unread,
Sep 17, 2012, 1:12:36 PM9/17/12
to zaproxy...@googlegroups.com, Guifre Ruiz Utges
The Session Fixation scanner raises alerts on various aspects of sessions as well.. including if the the session is "insecurely transmitted".

If we are doing more work in that area, we should definitely take it into account, so that we don't end up with disparate login in different areas doing the same thing in slightly different ways.  I have no issue with the Session Fixation scanner being merged / replaced by something else, if that is the right/best thing to do, by the way.

C



--
 
 

Guifre

unread,
Sep 17, 2012, 4:01:53 PM9/17/12
to colm.p.o...@gmail.com, zaproxy...@googlegroups.com
Hello,

I think we could get rid of those false positives/negatives that Colm
said by doing an entropy estimation of the token value. If it has a
low randomness such as a counter or it matches the session id, then it
is either vulnerable to guessing attacks or not an anti-csrf token in
both cases the alert is raised.

I think the main limitation in the current design is what Simon
pointed out about not being able to request again a given page after
starting a new session if it requires user authentication.

BTW, it is awesome that you studied at the UAB, there are a very few
international students.

Best Regards,
Guifre.

Guifre Ruiz Utges

unread,
Oct 3, 2012, 5:53:28 AM10/3/12
to zaproxy...@googlegroups.com, colm.p.o...@gmail.com
Hello,


I have been working for a while on this extension and I implemented the following features:

Entropy estimation: After performing the second request, it is carried out a randomness estimation (by computing the Levenshtein distance ratio)  of both of the token values. I defined a threshold and if it is lower than it the token is either not random enough or not an anti-csrf token. 

Request status; I implemented another function that tries to determine if the second request (without cookies) is either properly carried out or the server is redirecting us to the login page. I perform this check by looking at the HTTP code status.



On the other hand, I performed a set of tests in both demo apps and real world apps, these are the results:


Facebook

163155 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The page has parameters marked temporary vulnerable
163156 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: lsd, AVpYQ7RX
163156 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: default_persistent, 0
163156 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: next, http://www.facebook.com/directory/people/A-80081761-80772120
163156 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: charset_test, €,´,€,´,水,Д,Є
163156 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: timezone, 
163156 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: lgnrnd, 020036_NZ89
163156 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: lgnjs, n
163156 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: locale, es_ES
163156 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: o, 2048
163156 [Thread-178] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: init, dir
200
163348 [Thread-177] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - the distance between "AVogYVkR" and "AVqTjOmD" is 6 and their ratio is: 0.75
163348 [Thread-177] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The distance between "AVogYVkR" and "AVqTjOmD" is 6 and their ratio is: 0.75, random enough
163348 [Thread-177] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Found Anti-CSRF token: lsd, AVqTjOmD
163348 [Thread-177] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - the distance between "020038_ST8S" and "020208_v1NR" is 6 and their ratio is: 0.5454545454545454
163348 [Thread-177] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The distance between "020038_ST8S" and "020208_v1NR" is 6 and their ratio is: 0.5454545454545454, random enough
163348 [Thread-177] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Found Anti-CSRF token: lgnrnd, 020208_v1NR

"lsd" is a random anti-csrf token as explained in  http://www.pallmallmedical.co.uk/cookies
I could not find information in google about the "lgnrnd" parameter but it also looks like a random anti-csrf token.


gmail:

1640998 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The page has parameters marked temporary vulnerable
1640998 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: continue, http://mail.google.com/mail/
1640998 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: service, mail
1640998 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: dsh, 7965555468608615868
1640998 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: ltmpl, default
1640998 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: GALX, Ldh-U9oUXrc
1640998 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: timeStmp, 
1640998 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: secTok, 
1640998 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: rmShown, 1
200
1641103 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - the distance between "7965555468608615868" and "2970808815108171516" is 14 and their ratio is: 0.7368421052631579
1641103 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The distance between "7965555468608615868" and "2970808815108171516" is 14 and their ratio is: 0.7368421052631579, random enough
1641103 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Found Anti-CSRF token: dsh, 2970808815108171516
1641103 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - the distance between "Ldh-U9oUXrc" and "NywfjREgMcg" is 11 and their ratio is: 1.0
1641103 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The distance between "Ldh-U9oUXrc" and "NywfjREgMcg" is 11 and their ratio is: 1.0, random enough
1641103 [Thread-437] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Found Anti-CSRF token: GALX, NywfjREgMcg

"dsh" is a random anti-csrf token as explained in http://nagarun.wordpress.com/2011/07/02/my-mail-to-google-on-csrf/
"GALX" is a random anti-csrf token as explained in http://userscripts.org/topics/36200 
0% of false positives (I can not say about false negatives because I dont know about all their protections)


bodgeit

156931 [Thread-18] INFO org.parosproxy.paros.core.scanner.HostProcess  - start host http://localhost:10001 | Csrftokenscan level HIGH
157018 [Thread-26] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The page has parameters marked temporary vulnerable
157023 [Thread-26] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: null, 
157023 [Thread-26] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: anticsrf, 0.02386741959595151
157160 [Thread-26] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - the distance between "0.02386741959595151" and "0.45802788970587016" is 14 and their ratio is: 0.7368421052631579
157160 [Thread-26] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The distance between "0.02386741959595151" and "0.45802788970587016" is 14 and their ratio is: 0.7368421052631579, random enough
157160 [Thread-26] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Found Anti-CSRF token: anticsrf, 0.45802788970587016

"anticsrf" is a random anti-csrf token
0% of false positives, 0% of false negatives 


Another anti-csrf testing app I found:

18136 [Thread-19] INFO org.parosproxy.paros.core.scanner.HostProcess  - start host http://aopcgr.uab.es:10001 | Csrftokenscan level HIGH
18171 [Thread-22] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The page has parameters marked temporary vulnerable
18171 [Thread-22] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: a, 
18171 [Thread-22] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: action, 
18173 [Thread-24] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The page has parameters marked temporary vulnerable
18175 [Thread-24] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: csrf_token, MTM0OTE5MTE3MEYyaXNMMjUxYmZYVDFUWW1NSjAxczVoVkY1cUMxemxO
18175 [Thread-24] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Input Tag: csrf_token, whateverkey
18179 [Thread-22] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - New alert pluginid=20012 None. Warning only. uri=http://aopcgr.uab.es:10001/hpp/
18180 [Thread-24] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - the distance between "whateverkey" and "MTM0OTE5MTE4MjFPQUJYNG9pWmxOTlB1N2d6R2J1YldGQlZNMTQ0Ukpu" is 55 and their ratio is: 0.9821428571428571
18180 [Thread-24] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - The distance between "whateverkey" and "MTM0OTE5MTE4MjFPQUJYNG9pWmxOTlB1N2d6R2J1YldGQlZNMTQ0Ukpu" is 55 and their ratio is: 0.9821428571428571, random enough
18181 [Thread-24] DEBUG org.zaproxy.zap.extension.csrftokenscan.Csrftokenscan  - Found Anti-CSRF token: csrf_token, MTM0OTE5MTE4MjFPQUJYNG9pWmxOTlB1N2d6R2J1YldGQlZNMTQ0Ukpu

"csrf_token" is a random anti-csrf token
0% of false positives, 0% of false negatives 


I also carried out other tests with same results. IMAHO, I think this initial results are quite promising, I will write more information about it ASAP. Should I put the jar file available in the downloads page?


Best Regards,
Guifre
Reply all
Reply to author
Forward
0 new messages