Ausnutzbare C # -Funktionen [geschlossen]


69

Diese Frage ähnelt den ausnutzbaren PHP-Funktionen .

Beschmutzte Daten stammen vom Benutzer oder genauer gesagt von einem Angreifer. Wenn eine verschmutzte Variable eine Senkenfunktion erreicht, liegt eine Sicherheitsanfälligkeit vor. Zum Beispiel ist eine Funktion, die eine SQL-Abfrage ausführt, eine Senke, und GET / POST-Variablen sind Quellen von Verschmutzungen.

Was sind alle Senkenfunktionen in C #? Ich suche nach Funktionen, die eine Sicherheitslücke oder Softwareschwäche verursachen . Ich interessiere mich besonders für Sicherheitslücken bei der Remotecodeausführung. Gibt es ganze Klassen / Bibliotheken, die böse Funktionen enthalten, auf die ein Hacker Einfluss nehmen möchte? Wie machen Leute versehentlich gefährlichen C # -Code?


5
Es ist ein bisschen vage. Ein Großteil der Gefahr geht von Anwendungen aus, die unter voller Vertrauenswürdigkeit anstatt unter einer niedrigeren Vertrauensstufe ausgeführt werden. Je geringer das Vertrauen, desto schwieriger wird es, etwas Bösartiges zu tun.
Lucero

@Lucero Ich mochte die PHP-Frage, ich denke, das trifft ziemlich gut auf C # zu.
Turm

2
Wow, der Software-Schwachstellen-Link ist eine riesige Seite.
IWriteApps

@Gio yep hat eine Weile gebraucht, um zusammenzustellen;)
Turm

3
Was genau erwarten Sie hier? Eine riesige Liste von Funktionen? Ich bin mir nicht sicher , wie das nützlich ist ... Auch sein Pedant, Sie sprechen über die .Net Base Class Library , oder vielleicht Asp.Net, nicht C # Funktionen (Ich bin nicht sicher , es gibt „C # Funktionen“ ).
Kobi

Antworten:


31

Auf der webbasierten Seite ist C # (und allgemeiner ASP.NET) häufig anfällig für Folgendes (Elemente, die von OWASP Top 10 2013 aufgelistet werden) ). Mir ist klar, dass Sie hauptsächlich an Senkenfunktionen interessiert waren, von denen ich einige behandele. Sie haben jedoch gefragt, wie Leute versehentlich gefährlichen C # -Code erstellen. Hoffentlich habe ich hier einen Einblick gegeben.

A1-Injektion

SQL-Injektion

Generieren von Abfragen durch Verkettung von Zeichenfolgen.

var sql = "SELECT * FROM UserAccount WHERE Username = '" + username "'";
SqlCommand command = new SqlCommand(sql , connection);
SqlDataReader reader = command.ExecuteReader();

Dies kann häufig durch parametrisierte Abfragen gelöst werden. Wenn Sie jedoch eine INBedingung verwenden, ist dies derzeit nicht möglich ohne Verkettung von Zeichenfolgen .

LDAP-Injektion

Code wie

searcher.Filter = string.Format("(sAMAccountName={1})", loginName);

kann die Anwendung anfällig machen. Weitere Informationen hier .

OS Command Injection

Dieser Code ist anfällig für die Befehlsinjektion, da an den zweiten Parameter Process.Startzusätzliche Befehle übergeben werden können, wobei das &Zeichen zum Stapeln mehrerer Befehle verwendet wird

string strCmdText= @"/C dir c:\files\" + Request.QueryString["dir"];
ProcessStartInfo info = new ProcessStartInfo("CMD.exe", strCmdText);
Process.Start(info);

z.B foldername && ipconfig

A2-defekte Authentifizierung und Sitzungsverwaltung

Ausloggen

Die standardmäßige SignOut- Methode zur Formularauthentifizierung aktualisiert nichts auf der Serverseite, sodass ein erfasstes Authentifizierungstoken weiterhin verwendet werden kann.

Durch Aufrufen der SignOut-Methode wird nur das Formularauthentifizierungscookie entfernt. Der Webserver speichert keine gültigen und abgelaufenen Authentifizierungstickets für einen späteren Vergleich. Dies macht Ihre Site anfällig für einen Wiederholungsangriff, wenn ein böswilliger Benutzer ein gültiges Formularauthentifizierungscookie erhält.

Sitzungsstatus für die Authentifizierung verwenden

Eine Sicherheitsanfälligkeit bezüglich Sitzungsfixierung kann vorliegen, wenn ein Benutzer den Sitzungsstatus zur Authentifizierung verwendet hat .

A3-Cross-Site-Scripting (XSS)

Response.Write(und die Verknüpfung <%= =>) sind standardmäßig anfällig, es sei denn, der Entwickler hat daran gedacht, die Ausgabe in HTML zu codieren. Die neuere <%:HTML- Verknüpfung codiert standardmäßig, obwohl einige Entwickler diese möglicherweise verwenden, um Werte in JavaScript einzufügen, wo sie von einem Angreifer weiterhin maskiert werden können. Selbst mit dem modernen Razor-Motor ist es schwierig, dies richtig zu machen:

var name = '@Html.Raw(HttpUtility.JavaScriptStringEncode(Model.Name))';

ASP.NET aktiviert standardmäßig die Anforderungsüberprüfung , die alle Eingaben von Cookies, der Abfragezeichenfolge und von POST-Daten blockiert, die möglicherweise schädlich sein könnten (z. B. HTML-Tags). Dies scheint gut mit Eingaben umzugehen, die über die jeweilige App eingehen. Wenn jedoch Inhalte in der Datenbank vorhanden sind, die aus anderen Quellen eingefügt wurden, z. B. aus einer App, die mit anderen Technologien geschrieben wurde, ist es möglich, dass weiterhin schädlicher Skriptcode ausgegeben wird. Eine weitere Schwachstelle besteht darin, dass Daten in einen Attributwert eingefügt werden. z.B

<%
alt = Request.QueryString["alt"];
%>
<img src="http://example.com/foo.jpg" alt="<%=alt %>" />

Dies kann ausgenutzt werden, ohne die Anforderungsvalidierung auszulösen:

Wenn altja

" onload="alert('xss')

dann rendert dies

<img src="http://example.com/foo.jpg" alt="" onload="alert('xss')" />

In alten Versionen von .NET war es für einen Entwickler ein kleines Minenfeld , sicherzustellen, dass seine Ausgabe mit einigen der Standard-Websteuerelemente korrekt codiert wurde.

Leider enthält die Datenbindungssyntax noch keine integrierte Codierungssyntax. Es kommt in der nächsten Version von ASP.NET

zB nicht verletzlich:

  <asp:Repeater ID="Repeater1" runat="server">
    <ItemTemplate>
      <asp:TextBox ID="txtYourField" Text='<%# Bind("YourField") %>'
        runat="server"></asp:TextBox>
    </ItemTemplate>
  </asp:Repeater>

anfällig:

<asp:Repeater ID="Repeater2" runat="server">
  <ItemTemplate>
    <%# Eval("YourField") %>
  </ItemTemplate>
</asp:Repeater>

A4-Unsichere direkte Objektreferenzen

Durch die MVC-Modellbindung können Parameter, die zu POST-Daten hinzugefügt wurden, auf ein Datenmodell abgebildet werden. Dies kann unbeabsichtigt geschehen, da der Entwickler nicht bemerkt hat, dass ein böswilliger Benutzer Parameter auf diese Weise ändern kann. Das BindAttribut kann verwendet werden, um dies zu verhindern .

A5-Sicherheitsfehlkonfiguration

Es gibt viele Konfigurationsoptionen, die die Sicherheit einer Anwendung beeinträchtigen können. Zum Beispiel voran customErrorszu Onoder ermöglicht Spur.

Scanner wie ASafaWeb können diese häufigen Fehlkonfigurationen überprüfen.

A6-sensible Datenexposition

Standard-Hashing

Die Standard-Kennwort-Hashing-Methoden in ASP.NET sind manchmal nicht die besten.

A7-Zugriffskontrolle auf fehlender Funktionsebene

Fehler beim Einschränken des URL-Zugriffs

Im integrierten Pipeline-Modus kann .NET jede Anforderung anzeigen und Handles können jede Anforderung auch für Nicht-.NET-Ressourcen (z .js. B. und Bilder) autorisieren . Wenn die Anwendung jedoch im klassischen Modus ausgeführt wird, sieht .NET nur Anforderungen an Dateien, z. B. weil .aspxandere Dateien möglicherweise versehentlich ungesichert sind. Siehe diese Antwort weitere Einzelheiten zu den Unterschieden.

zB www.example.com/images/private_photograph_user1.jpgist es wahrscheinlicher, dass es in einer Anwendung, die im klassischen Modus ausgeführt wird, anfällig ist, obwohl es Problemumgehungen gibt .

A8-Cross-Site Request Forgery (CSRF)

Obwohl die älteren Webformularanwendungen normalerweise sicherer gegen CSRF sind, da der Angreifer die Werte für den Ansichtsstatus und die Ereignisüberprüfung fälschen muss , können neuere MVC-Anwendungen anfällig sein, sofern der Entwickler keine Anti-Fälschungs-Token manuell implementiert hat . Hinweis Ich sage nicht, dass Webformulare nicht anfällig sind, sondern dass es schwieriger ist, nur einige grundlegende Parameter weiterzugeben. Es gibt jedoch Korrekturen, z. B. die Integration des Benutzerschlüssels in den Wert für den Ansichtsstatus.

Wenn die EnableEventValidation-Eigenschaft auf true festgelegt ist, überprüft ASP.NET, ob ein Steuerelementereignis von der Benutzeroberfläche stammt, die von diesem Steuerelement gerendert wurde. Ein Steuerelement registriert seine Ereignisse während des Renderns und überprüft dann die Ereignisse während der Nach- oder Rückrufbehandlung. Wenn ein Listensteuerelement beispielsweise beim Rendern der Seite die Optionen 1, 2 oder 3 enthält und eine Postback-Anforderung unter Angabe der Option 4 eingeht, löst ASP.NET eine Ausnahme aus. Alle ereignisgesteuerten Steuerelemente in ASP.NET verwenden diese Funktion standardmäßig.

Die Funktion [EnableEventValidation] verringert das Risiko nicht autorisierter oder böswilliger Postback-Anfragen und Rückrufe. Es wird dringend empfohlen, die Ereignisüberprüfung nicht zu deaktivieren.

A10-Unvalidated - Weiterleitungen und Weiterleitungen

Hinzufügen von Code wie

Response.Redirect(Request.QueryString["Url"]);

macht Ihre Website anfällig. Der Angriff kann durch Senden einer Phishing-E-Mail an einen Benutzer mit einem Link ausgelöst werden. Wenn der Benutzer wachsam ist, hat er möglicherweise die Domain der URL vor dem Klicken doppelt überprüft. Da die Domain jedoch mit Ihrer Domain übereinstimmt, der der Benutzer vertraut, klickt er auf den Link, ohne zu wissen, dass die Seite den Benutzer zur Domain des Angreifers umleitet.

Die Validierung sollte am erfolgen, Urlum sicherzustellen, dass es sich entweder um eine relative, zulässige URL oder eine absolute URL zu einer Ihrer eigenen zulässigen Domänen und Seiten handelt. Möglicherweise möchten Sie überprüfen, ob jemand Ihre Benutzer nicht weiterleitet /Logout.aspx. Obwohl ein Angreifer möglicherweise nicht daran gehindert wird, direkt auf ihn zu verlinken http://www.example.com/Logout.aspx, könnte er die Umleitung verwenden, um die URL auszublenden, sodass es für einen Benutzer schwieriger ist, zu verstehen, auf welche Seite zugegriffen wird (http://www.example.com/Redirect.aspx?Url=%2f%4c%6f%67%6f%75%74%2e%61%73%70%78 ).

Andere

Die anderen OWASP-Kategorien sind:

  • A9-Verwenden von Komponenten mit bekannten Sicherheitslücken

Ich kann mir keine vorstellen, die spezifisch für C # / ASP.NET sind. Ich werde meine Antwort aktualisieren, wenn mir welche einfallen (wenn Sie der Meinung sind, dass sie für Ihre Frage relevant sind).


Ich hätte das genauer lesen sollen.
Turm

@rook mich auch. Dieser Typ ist großartig. Und ich meine es ernst.
Royi Namir

55

Alles, was reguläre Ausdrücke verwendet (insbesondere der RegularExpressionValidator). Führen Sie dazu einen RegularExpressionValidator mit dem regulären Ausdruck aus^(\d+)+$ und geben Sie ihm 30 Ziffern und ein Alpha-Zeichen zum Überprüfen.

Einige Beiträge:

Dies wird als Denial-of-Service-Angriff mit regulären Ausdrücken bezeichnet und kann eine Website in die Knie zwingen.


3
Oh, das ist gut. Und gilt für jede Sprache mit Regex-Unterstützung.
Jeff Mercado

2
Interessante Kuriosität, aber dies ist weit entfernt von der vollständigen Antwort.
Turm

2
Alle regulären Ausdrücke verursachen solche Probleme nicht. Dieser ist besonders darauf ausgelegt, diese Art von Problem zu haben.
Lasse V. Karlsen

3
Ja, das habe ich getan und ich stimme voll und ganz zu. Wenn Sie reguläre Ausdrücke verwenden, die Sie nicht überprüft haben, und Sie viel über reguläre Ausdrücke wissen , öffnen Sie alle Arten von Dosen mit Würmern.
Lasse V. Karlsen

1
Durch die Verwendung eines intelligenten Algorithmus und das Entfernen der Unterstützung für einige exotische Funktionen kann Regex sicher durchgeführt werden. Beispiele: TRE, Googles RE2.
Jilles

18

Process.Start ist der erste, der mir in den Sinn kommt.

Ich bin sicher, dass WindowsIdentityund vieles davon System.Securityauch für das Böse verwendet werden kann.

Natürlich gibt es SQL-Injection-Angriffe, aber ich denke nicht, dass Sie das meinen (obwohl die Remote-Ausführung über SQL Server erfolgen kann).


2
Dinge wie Verbindungszeichenfolgen, die einen Benutzernamen und ein Kennwort für SQL haben, sind etwas zwielichtig. Wenn Sie statische Zeichenfolgen verwenden, werden diese normalerweise in Dekompilierern verwendet. Daher ist es immer am besten, sie nicht zu verwenden, wenn sie private Informationen enthalten.
Kyndigs

Bitte beschreiben Sie die Probleme mit WindowsIdentity
jgauffin

Ich bevorzuge zwar eine Verbindungszeichenfolge mit Benutzernamen und Kennwort, kontrolliere dann aber die Datenbankberechtigungen streng, sodass der Benutzername und das Kennwort nichts tun können, was ich ihm nicht ausdrücklich von Fall zu Fall gestattet habe.
Peter Lange

17

Abgesehen von dem Process.Start()bereits erwähnten Offensichtlichen sehe ich einige Möglichkeiten einer möglichen indirekten Ausbeutung.

  • WinAPI ruft über PInvoke zu CreateProcess()und so weiter.
  • Jede Art von dynamischem Lademechanismus für Baugruppen, der Assembly.Load()andere Überlastungen verwendet. Wenn eine kompromittierte Baugruppe es zum System geschafft und geladen hat.
  • Wenn Sie im Allgemeinen in vollem Vertrauen laufen.
  • Mit den richtigen Berechtigungen können Registrierungsvorgänge Sie gefährden.

Das ist alles, was mir gerade in den Sinn kommt.


12

IMO: Die ausnutzbaren Funktionen Nr. 1 sehen unschuldig aus, sind aber sehr gefährlich, wenn sie ohne Gedanken verwendet werden.

In ASP.Net Response.Writeoder der Verknüpfung:

  <%= searchTermFromUser %>

In ADO.Net:

  • Der string +Betreiber:
    var sql = "SELECT * FROM table WHERE name = '" + searchTermFromUser + "'"

4
Machen Sie niemals solche SQL-Anweisungen. Verwenden Sie Command.AddParameter und Parameternamen in Abfragen.
Jgauffin

1
Ja, das ist, was ich meine. Dies sind Beispiele für ausnutzbaren Code.
GvS

Was ist falsch an response.write für die Website, die das verwendet?
Xaqron

@ Xaqron: Weil es üblich ist, HtmlEncode
GvS

2
@Xaqron: Das ist ein Pfad, um [Cross Site Scripting] ( en.wikipedia.org/wiki/Cross-site_scripting )
zuzulassen

12

Alle Daten, die Sie vom Benutzer (oder einer anderen externen Quelle) erhalten und an ein anderes System oder einen anderen Benutzer weitergeben, sind ein potenzieller Exploit.

Wenn Sie eine Zeichenfolge vom Benutzer erhalten und sie einem anderen Benutzer anzeigen, ohne HtmlEncode zu verwenden, ist dies ein potenzieller Exploit.

Wenn Sie vom Benutzer eine Zeichenfolge erhalten und diese zum Erstellen von SQL verwenden, handelt es sich um eine potenzielle SQL-Injektion.

Wenn Sie vom Benutzer eine Zeichenfolge erhalten und damit einen Dateinamen für Process.Start oder Assembly.Load vergeben, handelt es sich um eine Sicherheitsanfälligkeit bezüglich Remoteausführung

Sie verstehen, die Gefahr besteht in der Verwendung nicht bereinigter Daten. Wenn Sie niemals Benutzereingaben an ein externes System weitergeben, ohne diese zu bereinigen (Beispiel: HtmlEncode) oder injektionssichere Schnittstellen verwenden (Beispiel: SQL-Parameter), sind Sie relativ sicher - in dem Moment, in dem Sie Vergessen Sie, etwas zu bereinigen. Die unschuldigste Methode kann zu einer Sicherheitslücke werden.

Hinweis: Cookies, HTML-Header und alles andere, was über ein Netzwerk übertragen wird, sind ebenfalls Daten des Benutzers. In den meisten Fällen sind sogar Daten in Ihrer Datenbank Daten des Benutzers.


Ja, Daten vom Benutzer werden als verdorbene Daten bezeichnet. Wenn verdorbene Daten eine "Senken" -Funktion erreichen, liegt eine Sicherheitslücke vor. Schauen Sie sich die Liste der PHP-Senkenfunktionen an, die ich oben gepostet habe.
Turm

1
@Rook - Sie können möglicherweise nicht alle "Senken" -Funktionen auflisten. Jeder Code, der nicht explizit für den Umgang mit möglicherweise schädlichen Daten entwickelt wurde, ist eine Senkenfunktion - auch jeder Code, der für den Umgang mit bösartigen Daten in einem diffrakten Kontext ausgelegt ist dass Sie es verwenden, ist eine Senkenfunktion (Beispiel: XmlDocument.Load wurde entwickelt, um fehlerhaftes oder böswilliges XML zu erkennen und abzulehnen. Wenn Sie jedoch den Benutzer die von Ihnen geladene Datei steuern lassen, können Sie damit Ihre Konfigurationsdateien laden und eine wirklich fehlerhafte erstellen Sicherheitslücke
Nir

Hast du meinen PHP-Link oben gesehen? Oder sind Sie auf statische Analysetools wie RATS ( fortify.com/ssa-elements/threat-intelligence/rats.html ) gestoßen
Turm

8

Viele Dinge im System.Net, System.XML, System.IO (alles, was einen URI- und / oder Dateipfad verwendet und sich tatsächlich mit der von ihnen identifizierten Ressource befasst) System.Reflection, System.Security, System.Web, System .Data und System.Threading-Namespaces können gefährlich sein, da sie dazu verwendet werden können, schlechte Dinge zu tun, die über das bloße Durcheinander der aktuellen Ausführung hinausgehen. So sehr, dass es zeitaufwändig wäre, zu versuchen, jeden zu identifizieren.

Natürlich muss jede Methode in allen Baugruppen von Drittanbietern als gefährlich angesehen werden, bis etwas anderes gezeigt wird. Wieder zeitaufwändiger.

Ich denke auch nicht, dass dies ein besonders fruchtbarer Ansatz ist. Das Erstellen einer Checkliste mit Funktionen funktioniert nur mit einer begrenzten Bibliothek oder mit einer großen Sprache, in der sich vieles, was in einer Bibliothek mit einer Sprache wie C # enthalten wäre, in der Sprache selbst befindet.

Es gibt einige klassisch gefährliche Beispiele wie Process.Start()oder irgendetwas, das einen anderen Prozess direkt ausführt, aber sie werden ausgeglichen, indem sie ganz offensichtlich gefährlich sind. Sogar ein relativ tollkühner und inkompetenter Codierer kann darauf achten, wenn er dies verwendet, während Daten, die für andere Methoden verwendet werden, nicht bereinigt werden.

Diese Datenbereinigung ist fruchtbarer als jede Liste von Funktionen. Werden Daten validiert, um offensichtlich falsche Eingaben zu entfernen (was auf einen Angriff zurückzuführen sein kann oder einfach ein Fehler sein kann), und werden sie für eine bestimmte Ebene in geeigneter Weise codiert und maskiert (es wird zu viel über "gefährliche" Zeichenfolgen gesprochen , 'verletze niemals jemanden, es ist 'für SQL nicht korrekt entkommen, was weh tun kann, wenn es sich tatsächlich auf einer SQL-Ebene befindet - der Job, der erforderlich ist, um sicherzustellen, dass die Daten dort korrekt eingehen, ist der gleiche wie der, um Exploits zu vermeiden). Sind die Ebenen, auf denen die Kommunikation mit etwas außerhalb des Codes fest ist? Werden URIs beispielsweise mit nicht untersuchten Eingaben erstellt? Wenn nicht, können Sie einige der am häufigsten verwendeten Methoden System.Net und System.XML in Löcher umwandeln.


7

Die Verwendung von unsicherem Code kann zu Problemen wie Zeigern führen. Microsoft hat hier einen guten Artikel über unsicheren Code bereitgestellt: http://msdn.microsoft.com/en-us/library/aa288474(VS.71).aspx


2
-1 für das Qualifizieren von Zeigern als Probleme ... was für ein Problem sind sie?
Jachguate

Sie sind unsicher, ziemlich offensichtlich, wenn sie falsch verwendet werden, können sie ausgenutzt werden, daher können sie ein Problem sein. Braucht man nicht einstein, um herauszufinden, dass man es tut?
Kyndigs

bei falscher Verwendung . Ihr Auto ist unsicher, wenn es falsch verwendet wird . Ein Küchenmesser ist unsicher, wenn es falsch verwendet wird. Für jemanden, der weiß, was er tut und wie er richtig verwendet wird , ist er jedoch nicht von Natur aus unsicher.
Jachguate

Genau aus diesem Grund habe ich in meinem Beitrag "kann verursachen" gesagt ... Ich habe nicht gesagt "wird verursachen".
Kyndigs

7

Reflection.Emit und CodeDom

Bearbeiten :

Wenn Sie Plugins oder Bibliotheken von Drittanbietern zulassen, die Threading verwenden, kann Ihre gesamte Anwendung heruntergefahren werden, es sei denn, Sie laden diese Bibliotheken / Plugins in eine separate Appdomain.


7

Wahrscheinlich enthält die Hälfte des Frameworks sehr beängstigende Funktionen. Ich selbst denke dasFile.WriteAllText() sehr beängstigend, da es jede Datei überschreiben kann, auf die der aktuelle Benutzer Zugriff hat.

Ein anderer Ansatz für diese Frage wäre, wie Sie die Sicherheit verwalten können. Der Artikel unter http://ondotnet.com/pub/a/dotnet/2003/02/18/permissions.html enthält eine grundlegende Beschreibung des .NET-Sicherheitssystems, wobei der System.Security.Permissions- Namespace alle Berechtigungen .NET enthält zur Verfügung gestellt. Sie können auch einen Blick auf http://msdn.microsoft.com/en-us/library/5ba4k1c5.aspx werfenWeitere Informationen .

Kurz gesagt, mit .NET können Sie die Berechtigungen eines Prozesses einschränken, z. B. Methoden verweigern, mit denen Daten auf der Festplatte geändert werden. Sie können diese Berechtigungen dann überprüfen und prüfen, ob der Prozess über sie verfügt oder nicht.


7

Selbst ein einfacher Zeichenfolgenvergleich kann ein Problem sein.

Wenn eine Anwendung basierend auf den Ergebnissen dieser String.Compare-Operation eine Vertrauensentscheidung trifft, kann das Ergebnis dieser Entscheidung durch Ändern der CurrentCulture unterlaufen werden

Schauen Sie sich das Beispiel an . Ziemlich leicht zu übersehen


3

Ich habe Code gesehen, in dem der Benutzer den Namen und die Parameter für einen Funktionsaufruf in einer Datenbank festlegen konnte. Das System würde dann die benannte Funktion durch Reflection ausführen, ohne etwas zu überprüfen ...

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.