Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# OAuth batch multipart content response, how to get all the contents not as string objects

I'm receiving a multipart content response that belongs to an OAuth batch request:

// batchRequest is a HttpRequestMessage, http is an HttpClient
HttpResponseMessage response = await http.SendAsync(batchRequest);

If I read its content as full text:

string fullResponse = await response.Content.ReadAsStringAsync();

This is what it contains:

--batchresponse_e42a30ca-0f3a-4c17-8672-22abc469cd16
Content-Type: application/http
Content-Transfer-Encoding: binary

HTTP/1.1 200 OK
DataServiceVersion: 3.0;
Content-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8

{\"odata.metadata\":\"https://graph.windows.net/XXX.onmicrosoft.com/$metadata#directoryObjects/@Element\",\"odata.type\":\"Microsoft.DirectoryServices.User\",\"objectType\":\"User\",\"objectId\":\"5f6851c3-99cc-4a89-936d-4bb44fa78a34\",\"deletionTimestamp\":null,\"accountEnabled\":true,\"signInNames\":[],\"assignedLicenses\":[],\"assignedPlans\":[],\"city\":null,\"companyName\":null,\"country\":null,\"creationType\":null,\"department\":\"NRF\",\"dirSyncEnabled\":null,\"displayName\":\"dummy1 Test\",\"facsimileTelephoneNumber\":null,\"givenName\":\"dummy1\",\"immutableId\":null,\"isCompromised\":null,\"jobTitle\":\"test\",\"lastDirSyncTime\":null,\"mail\":null,\"mailNickname\":\"dummy1test\",\"mobile\":null,\"onPremisesSecurityIdentifier\":null,\"otherMails\":[],\"passwordPolicies\":null,\"passwordProfile\":{\"password\":null,\"forceChangePasswordNextLogin\":true,\"enforceChangePasswordPolicy\":false},\"physicalDeliveryOfficeName\":null,\"postalCode\":null,\"preferredLanguage\":null,\"provisionedPlans\":[],\"provisioningErrors\":[],\"proxyAddresses\":[],\"refreshTokensValidFromDateTime\":\"2016-12-02T08:37:24Z\",\"showInAddressList\":null,\"sipProxyAddress\":null,\"state\":\"California\",\"streetAddress\":null,\"surname\":\"Test\",\"telephoneNumber\":\"666\",\"[email protected]\":\"directoryObjects/5f6851c3-99cc-4a89-936d-4bb44fa78a34/Microsoft.DirectoryServices.User/thumbnailPhoto\",\"usageLocation\":null,\"userPrincipalName\":\"[email protected]\",\"userType\":\"Member\"}
--batchresponse_e42a30ca-0f3a-4c17-8672-22abc469cd16
Content-Type: application/http
Content-Transfer-Encoding: binary

HTTP/1.1 200 OK
DataServiceVersion: 3.0;
Content-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8

{\"odata.metadata\":\"https://graph.windows.net/XXX.onmicrosoft.com/$metadata#directoryObjects/@Element\",\"odata.type\":\"Microsoft.DirectoryServices.User\",\"objectType\":\"User\",\"objectId\":\"dd35d761-e6ed-44e7-919f-f3b1e54eb7be\",\"deletionTimestamp\":null,\"accountEnabled\":true,\"signInNames\":[],\"assignedLicenses\":[],\"assignedPlans\":[],\"city\":null,\"companyName\":null,\"country\":null,\"creationType\":null,\"department\":null,\"dirSyncEnabled\":null,\"displayName\":\"Max Admin\",\"facsimileTelephoneNumber\":null,\"givenName\":null,\"immutableId\":null,\"isCompromised\":null,\"jobTitle\":null,\"lastDirSyncTime\":null,\"mail\":null,\"mailNickname\":\"maxadmin\",\"mobile\":null,\"onPremisesSecurityIdentifier\":null,\"otherMails\":[],\"passwordPolicies\":null,\"passwordProfile\":null,\"physicalDeliveryOfficeName\":null,\"postalCode\":null,\"preferredLanguage\":null,\"provisionedPlans\":[],\"provisioningErrors\":[],\"proxyAddresses\":[],\"refreshTokensValidFromDateTime\":\"2016-12-05T15:11:51Z\",\"showInAddressList\":null,\"sipProxyAddress\":null,\"state\":null,\"streetAddress\":null,\"surname\":null,\"telephoneNumber\":null,\"[email protected]\":\"directoryObjects/dd35d761-e6ed-44e7-919f-f3b1e54eb7be/Microsoft.DirectoryServices.User/thumbnailPhoto\",\"usageLocation\":null,\"userPrincipalName\":\"[email protected]\",\"userType\":\"Member\"}
--batchresponse_e42a30ca-0f3a-4c17-8672-22abc469cd16--

I need to get all these contents as objects (like classics HttpResponseMessage, not simple strings), in order to get the HTTP return code, the JSON content, etc. as properties and be able to treat them.

I know how to read separatly all these contents, but I can't figure how to get them as objects, I've only succeeded in getting a string content :

var multipartContent = await response.Content.ReadAsMultipartAsync();
foreach (HttpContent currentContent in multipartContent.Contents) {
     var testString = currentContent.ReadAsStringAsync();
     // How to get this content as an exploitable object?
}

In my example, testString contains:

HTTP/1.1 200 OK
DataServiceVersion: 3.0;
Content-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8

{\"odata.metadata\":\"https://graph.windows.net/XXX.onmicrosoft.com/$metadata#directoryObjects/@Element\",\"odata.type\":\"Microsoft.DirectoryServices.User\",\"objectType\":\"User\",\"objectId\":\"5f6851c3-99cc-4a89-936d-4bb44fa78a34\",\"deletionTimestamp\":null,\"accountEnabled\":true,\"signInNames\":[],\"assignedLicenses\":[],\"assignedPlans\":[],\"city\":null,\"companyName\":null,\"country\":null,\"creationType\":null,\"department\":\"NRF\",\"dirSyncEnabled\":null,\"displayName\":\"dummy1 Test\",\"facsimileTelephoneNumber\":null,\"givenName\":\"dummy1\",\"immutableId\":null,\"isCompromised\":null,\"jobTitle\":\"test\",\"lastDirSyncTime\":null,\"mail\":null,\"mailNickname\":\"dummy1test\",\"mobile\":null,\"onPremisesSecurityIdentifier\":null,\"otherMails\":[],\"passwordPolicies\":null,\"passwordProfile\":{\"password\":null,\"forceChangePasswordNextLogin\":true,\"enforceChangePasswordPolicy\":false},\"physicalDeliveryOfficeName\":null,\"postalCode\":null,\"preferredLanguage\":null,\"provisionedPlans\":[],\"provisioningErrors\":[],\"proxyAddresses\":[],\"refreshTokensValidFromDateTime\":\"2016-12-02T08:37:24Z\",\"showInAddressList\":null,\"sipProxyAddress\":null,\"state\":\"California\",\"streetAddress\":null,\"surname\":\"Test\",\"telephoneNumber\":\"666\",\"[email protected]\":\"directoryObjects/5f6851c3-99cc-4a89-936d-4bb44fa78a34/Microsoft.DirectoryServices.User/thumbnailPhoto\",\"usageLocation\":null,\"userPrincipalName\":\"[email protected]\",\"userType\":\"Member\"}

I can't just imagine to parse manually this string... So if someone has a clue or can explain me the good way to read the content, it would be nice.

Thanks, Max

like image 669
Max Xapi Avatar asked Jan 20 '17 09:01

Max Xapi


1 Answers

Here is a way it can be done. The key is to add a new content type "msgtype" header to the response:

var multipartContent = await response.Content.ReadAsMultipartAsync();
var multipartRespMsgs = new List<HttpResponseMessage>();
foreach (HttpContent currentContent in multipartContent.Contents) {
    // Two cases:
    // 1. a "single" response
    if (currentContent.Headers.ContentType.MediaType.Equals("application/http", StringComparison.OrdinalIgnoreCase)) {
        if (!currentContent.Headers.ContentType.Parameters.Any(parameter => parameter.Name.Equals("msgtype", StringComparison.OrdinalIgnoreCase) && parameter.Value.Equals("response", StringComparison.OrdinalIgnoreCase))) {
            currentContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("msgtype", "response"));
        }
        multipartRespMsgs.Add(await currentContent.ReadAsHttpResponseMessageAsync());
        // The single object in multipartRespMsgs contains a classic exploitable HttpResponseMessage (with IsSuccessStatusCode, Content.ReadAsStringAsync().Result, etc.)
    }
    // 2. a changeset response, which is an embedded multipart content
    else {
        var subMultipartContent = await currentContent.ReadAsMultipartAsync();
        foreach (HttpContent currentSubContent in subMultipartContent.Contents) {
            currentSubContent.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("msgtype", "response"));
            multipartRespMsgs.Add(await currentSubContent.ReadAsHttpResponseMessageAsync());
            // Same here, the objects in multipartRespMsgs contain classic exploitable HttpResponseMessages
        }
    }
}

Thanks to darl0026

like image 77
Max Xapi Avatar answered Oct 12 '22 05:10

Max Xapi