After some clever deductive reasoning, a.k.a randomly unplugging cables from the router, I determined that my TV was sending these mystery frames (yes, my TV — I have a Sony X805D Android TV). After power cycling the TV the problem went away but of course I wanted to figure out what was actually happening. You’d be forgiven if the above frames aren’t immediately recognizable — their definition is buried deep in Appendix 31B of the IEEE 802.3 Ethernet standard.

The type of an Ethernet frame is determined by it’s EtherType, which is a two byte identifier that comes after two six byte MAC addresses denoting source and destination. The mystery frame’s EtherType was 0x8808, which is for Ethernet flow control.

The very existence of Ethernet flow control may come as a shock, especially since protocols like TCP have explicit flow control mechanisms, presumably to compensate for Ethernet’s lack of one. However, on page 752 of the Ethernet spec we find a section dedicated to (rudimentary) flow control. The frame structure is fairly bare-bones: a two byte “opcode”, which in this case is 0x0001 for “PAUSE” and a two byte “pause_time”, denoting increments of 512 bit times (here’s a great diagram of the frame).

To test out the behavior of pause frames more thoroughly I wrote a simple libpcap (or WinPcap) program that transmits a PAUSE frame every ten milliseconds.

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pcap/pcap.h> int main(int argc, char **argv) { unsigned char PAUSE_FRAME[60] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x01, /* Destination MAC: Spanning tree for bridges */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Source MAC: Null */ 0x88, 0x08, /* EtherType: MAC control */ 0x00, 0x01, /* OpCode: Pause */ 0xFF, 0xFF, /* pause_time: 65535 */ /* 42 bytes of padding */ }; /* OMITTED: OPEN UP THE INTERFACE */ memset(PAUSE_FRAME + 18, 0, 42); /* fill padding with 00 */ while (1) { if (pcap_sendpacket(fp, PAUSE_FRAME, sizeof(PAUSE_FRAME)) != 0) { fprintf(stderr, "Error sending frame: %s\n", pcap_geterr(fp)); break; } usleep(SLEEP_TIME_MS * 1000); } pcap_close(fp); return 0; }

Sure enough, sending this frame repeatedly killed all traffic on my home network. (You can check out the full code on GitHub). What’s interesting is that this may have arisen from a bug in my home router (TP-Link AC750 Archer C2 running firmware 0.9.1.3.2). According to the Ethernet spec (31B.1)

The globally assigned 48-bit multicast address 01-80-C2-00-00-01 has been reserved for use in MAC Control PAUSE frames for inhibiting transmission of data frames from a DTE in a full duplex mode IEEE 802.3 LAN. IEEE 802.1D-conformant bridges will not forward frames sent to this multicast destination address, regardless of the state of the bridge’s ports, or whether or not the bridge implements the MAC Control sublayer.

It would appear that there is a clause that specifically attempts to deal with this scenario: nodes sending PAUSE message to the special multicast address `01:80:C2:00:00:01`

are instructing the switch to not send them any more frames. My switch seems to honor this, but also forwards the frames to the other nodes on the network, in effect telling THEM to pause in sending frames, which would explain the observed behavior.

I did some digging — my router uses a MediaTek MT7620A router SoC which relies on a Realtek RTL8367RB to perform switch duties. Unfortunately I couldn’t find the data sheet for this specific chip, although the source code for the router is GPL, so the driver itself is perusable. A data sheet for the RTL8366 (a 6/9 port version of the chip) says on page 22:

Frames with group MAC address 01-80-c2-00-00-01 (802.3x Pause) and 01-80-c2-00-00-02 (802.3ad LCAP) will always be filtered. MAC address 01-80-c2-00-00-03 will always be forwarded. This function is controlled by pin strapping of EN_MLT_FWD (pin 32) upon power reset. After power on, the configuration may be changed by MLTID_ST[15:0][1:0] in Register MCPCR0-1 (0x000F – 0x0010).

Have we reached the end of the road? The above seems to suggest that the forwarding of PAUSE frames is controlled both by a pin and a register on the Ethernet switch chip. It would appear on my router this specific (standard conformant) feature was accidentally disabled, either by a floating pin or a zeroed out register leading to a “frame of death” that was forwarded by my switch, killing the network. It’s amazing what you find when you dig!

]]>The task was fairly straight forward, so I fired up Visual Studio 2015 and got to work. I usually target x64 during development (due to some misguided belief that the code will be faster), and when I ran the code in release mode I received the following output as the time needed to calculate the 42nd Fibonacci number:

Recursive: 0.977294758 seconds

Iterative: 0.000000310 seconds

Since calculating $F_{42}$ through naive recursion requires ~866 million function calls, this pretty much jived with my expectations. I was ready to submit the assignment and close up shop, but I decided it’d be safer to submit the executable as as 32-bit application. I switched over to x86 in Visual Studio, and for good measure ran the program again.

Recursive: 0.000000000 seconds

Iterative: 0.000000311 seconds

Well then. That was… suspiciously fast. For reference, here is (a stripped down version of) the code I was using.

#include <iostream> #include <iomanip> #include <chrono> #include <cassert> constexpr int MAX_FIB_NUM = 42; constexpr int F_42 = 267914296; int fib_recursive(int n) { if (n < 2) return n; else return fib_recursive(n - 1) + fib_recursive(n - 2); } int fib_iterative(int n) { int f_1 = 1, f_2 = 0; for (int i = 1; i < n; ++i) { int tmp = f_1; f_1 = f_1 + f_2; f_2 = tmp; } return f_1; } double measure_execution_time(int(*func)(int)) { auto start = std::chrono::high_resolution_clock::now(); int ret = func(MAX_FIB_NUM); auto end = std::chrono::high_resolution_clock::now(); assert(ret == F_42); return std::chrono::duration<double>(end - start).count(); //convert to fractional seconds } int main(int argc, char** argv) { auto recursive_duration = measure_execution_time(fib_recursive); auto iterative_duration = measure_execution_time(fib_iterative); std::cout << std::setprecision(9) << std::fixed; //up to nanoseconds std::cout << "Recursive: \t" << recursive_duration << " seconds " << std::endl; std::cout << "Iterative: \t" << iterative_duration << " seconds " << std::endl; return 0; }

In debug mode the code took the expected amount of time; only the release build targeting x86 was exhibiting the seemingly blazingly fast performance. What was happening here? Some constexpr magic resulting in the compiler precomputing the answer? An overly aggressive reordering of the

now()calls?

To figure out the answer I opened the executable in IDA and started poking around.

No wonder the code took almost no time — we’re simply measuring the time it takes to measure the lea instruction! The next section of code appeared to be the

fib_iterativefunction:

It would appear that a function pointer is no barrier to Visual Studio’s inlining;

measure_execution_timenever explicitly appears as a discrete subroutine. Regardless, the inlined assembly

fib_iterativeis about has straightforward as possible. Over on x64 the code appears even simpler (all code was compiled with /O2).

The function pointer inlining is gone, replaced with the more or less expected code, i.e. load the address of the function into an argument register and then call

measure_execution_time.

So what’s the deal here? Where the heck did

fib_recursivego on x86? I believe what we’re seeing is an unexpected application of dead code elimination. On Visual Studio the

assertmacro is

#define assert(expression) ((void)0)in release mode, meaning the check that the return is equal to

F_42turns into nothing!

Since the return of

fib_recursive(now) isn’t used, and the function itself simply does trivial math (besides calling itself), the compiler has decided it serves no purpose. What’s interesting is that the compiler did

fib_iterative. Given the choice between the two I would have assumed that

fib_iterative, with its constant sized loop, would be easier to analyze than the recursive structure of

fib_recursive. What’s even weirder is that this only happens on x86, not x64.

After modifying the code to display the the result of the functions with

std::coutthe problem went away. The moral of the story is that if you’re doing some performance unit testing make sure that your functions touch the outside world in some way; asserts aren’t always enough. Otherwise, the compiler may spontaneously decide to eliminate your code all together (and it may be platform dependent!), giving the illusion of incredible speed and cleverness on your part ]]>

Here in southeast Michigan nearly all of our electricity (and a good chunk of our natural gas) comes from DTE Energy, which serves 2.1 million people in the greater Metro Detroit area. DTE recently upgraded most of their electricity meters to ZigBee-enabled smart meters, and as part of this rollout they released the DTE Energy Insight app which allows customers to view their energy usage, set targets, and earn a host of achievements (no Steam cards sadly) when meeting different energy goals. In addition, at no charge DTE sends customers an “Energy Bridge”, a small device that connects to a home network and monitors the ZigBee messages generated by a smart meter to give real-time energy consumption information.

Given my curious nature I decided to poke around to discover how exactly the app and the Energy Bridge worked. This post is about a vulnerability in the app itself (although I’ve been tinkering with my Ettus Research B200 SDR to intercept the ZigBee messages as well).

By rooting my phone and using ProxyDroid to forward all traffic to a mitmproxy proxy running on my PC I deduced that the Insight app was attempting to connect to `apps.dteenergy.com`

via TLS (non-TLS connections were rejected). Even though I had the certificate authority that mitmproxy generates installed to the trust store on my phone the app refused to communicate through the TLS proxy, so naturally I suspected some form of certificate pinning or a custom trust store was being used by the app.

Decompiling the app’s APK with Apktool proved a somewhat frustrating experience; the app appeared to have been run through an obfuscator. The `res/raw/`

directory in the APK did provide hints, though; the app contained two .bks (BouncyCastle keystore) files. Unfortunately, the keystores were password protected. However, the resource IDs for these files gave me an anchor in the code, and I was able to follow the decompiled code to the function that loads the keystores through `javax.net.ssl.TrustManagerFactory`

.

`res/raw/dtecomodo.bks`

had resource ID `0x7f070009`

, and the decompilation clearly showed that the password “vectorform” was being used. This code existed in the `com.vectorform.wattsonandroid.c.a`

class, which let me know that Vectorform developed the app for DTE.

The keystore itself contained a certificate chain for the AddTrust External CA Root as well as the Comodo High-Assurance Secure Server CA, an intermediate authority. So, the Insight app wasn’t specifically pinning to a certificate for the API endpoint, but it enforced that the certificate that `apps.dteenergy.com`

presented must be issued by the specific AddTrust/Comodo chain given in the file. To bypass this restriction I added the mitmproxy root CA to the keystore and recompiled the app with Apktool.

The modified APK communicated through the mitmproxy — success!

Every API endpoint required that an HTTP Basic Access Authentication header was provided that contained the DTE customer’s username and password (the same one they use to access their online billing). The `IdentityService`

endpoint returned a `dteSAML`

variable, which needed to be included in all requests to endpoints that queried the customer’s actual energy usage. Presumably this is a SAML token, which likely is passed along to backend DTE servers that actually monitor the customer’s usage. This led me to believe that data for the application is managed separately from the actual usage data. This was further confirmed by investigating the `api/Customer`

endpoint which returned a `DTEID`

that could be used in some requests; `dteSAML`

was only needed when querying actual usage data.

Basic tests such as requesting information for a different `DTEID`

via a GET to `api/Customer`

showed that most endpoints were correctly checking access controls. Of interest was the `api/Notification`

endpoint, which accepted a curious `filter`

parameter. Un-URL-encoded, the parameter read as follows:

`DTEID eq <dteid> and IsRead eq false and NotificationType.IsNotification eq true`

This suggested that the `filter`

parameter accepted arbitrary queries against a JSON-like database and returned the results. I wrote a script to request arbitrary filter parameters; the only authorization needed was the username and password for my DTE account passed as a Basic Access Authentication header.

As suspected, the `filter`

parameter was essentially a read-only SQL injection attack; the server would respond with whatever was asked of it. Thus, a filter of `Customer.Zipcode eq 48346`

would return the app’s database for every user with a 48346 ZIP code. In addition to `api/Notification`

there were a number of other endpoints that also accepted a `filter`

parameter, e.g. `api/CustomerProject`

. This resulted in the compromise of the entire database.

Classic SQL injection attacks rely on string manipulation to escape the value of a parameter and modify the behaviour of the underlying query. While not as serious as a full injection vulnerability (which would allow us to invoke the ghost of Bobby Tables), allowing an authenticated user to specify the full parameter to a WHERE-like clause is nearly as dangerous (especially if the table contains personal data on every user of your app!).

If I may editorialize, DTE Energy Insight is a pretty slick app. It’s clear that a lot effort was put into its user interface and design. Although this article doesn’t cover the Energy Bridge device, my tinkering with it has shown me that the engineers that worked on it were security contentious. The endpoints that deal with customer energy information require a SAML token, and those such as `api/Customer`

don’t return information if a `DTEID`

different than that of the logged in user is requested.

The filter parameter was likely a dirty hack — I’m sure there’s an engineer somewhere that cringed when they wrote it. Although the app is relatively obscure in the grand scheme of things the personal information of perhaps hundreds of thousands of users will always be a juicy enough target to warrant malicious activity. Techniques such as certificate pinning or custom trust chains protect against nefarious men-in-the-middle; they can’t guarantee the secrecy of an insecure API.

- Jan 16, 2016: Vulnerability discovered
- Jan 28, 2016: Private disclosure to custserviceweb@dteenergy.com and social@vectorform.com
- Jan 29, 2016: Disclosure to CERT
- Feb 3, 2016: CERT confirmed reception of report by DTE
- Feb 29, 2016: CERT reported that DTE fixed the vulnerability
- Feb 29, 2016: Fix confirmed
- Mar 1, 2016: Public disclosure scheduled for Mar 3, 2016
- Mar 2, 2016: DTE requested disclosure be pushed back to Mar 10, 2016
- Mar 10, 2016: Public disclosure (VU#713312, CVE-2016-1562)

Go ahead and grab the latest APK or get it straight from Google Play.

]]>It’s up on github: MissionControl.

]]>Here are the slides for my talk: Scheduling: The first academic paper about Penguicon

A pre-print of the actual paper: Scheduling a conference to minimize RSVPs

]]>Download PDF: Foundations of the golden ratio base

]]>Blaise Pascal first introduced the triangle that would later come to hold his name [1], although in modern notations the so-called binomial coefficient denoted by $n \choose k$ may be more familiar to the reader. We shall prove a few interesting results regarding a sequence of the $n$-th root of means of the set of binomial coefficients

\begin{equation}\label{binomial}

{n \choose 0}, {n \choose 1}, {n \choose 2}, {\cdots}, {n \choose n}.

\end{equation}

In particular, if $A_n$ is the arithmetic mean of (\ref{binomial}) and $G_n$ is the geometric mean of (\ref{binomial}), we will show that the infinite sequences

\begin{equation*}

S_A = A_1, \sqrt{A_2}, \sqrt[3]{A_3}, \sqrt[4]{A_4}, \cdots, \quad S_G = G_1, \sqrt{G_2}, \sqrt[3]{G_3}, \sqrt[4]{G_4}, \cdots

\end{equation*}

converge to $2$ and $\sqrt{e}$ respectively.

Let $P_n$ be the sum of the of sequence (\ref{binomial}), i.e.

\begin{equation*}

P_n = {n \choose 0} + {n \choose 1} + {n \choose 2} + {\cdots} + {n \choose n} = \sum\limits_{i=0}^{n}{n \choose i}.

\end{equation*}

Recall that

\begin{equation*}

{j \choose k} = {j-1 \choose k-1} + {j-1 \choose k}.

\end{equation*}

If we let $P_n = 0$ for $n < 0$ and let $P^m_n$ be the $m$th term of $P_n$, then
\begin{equation}
P^m_n = P^{m-1}_{n-1} + P^{m}_{n-1},
\end{equation}
which is to say that every term of $P_n$ is composed of terms of $P_{n-1}$. In fact, every term of $P_{n-1}$ is used twice by $P_n$; for example $P^0_{n} = P^0_{n-1}$, $P^1_{n} = P^0_{n-1} + P^1_{n-1}$, $P^2_{n} = P^1_{n-1} + P^2_{n-1}$, $P^3_{n} = P^2_{n-1} + P^3_{n-1}$. Since $P_n$ contains $n+1$ terms, all $n$ terms of $P_{n-1}$ will be used exactly twice, and therefore
\begin{equation}
P_n = 2P_{n-1}.
\end{equation}
Lastly since $P_0 = 1 = 2^0$ we arrive at
\begin{equation} \label{pn}
P_n=2^n.
\end{equation}
We wish to find the arithmetic mean of $P_n$, which contains $n+1$ terms, so
\begin{equation}
A_n = \dfrac{P_n}{n+1}.
\end{equation}
Equipped with (\ref{pn}) we can see that $S^n_A$, the $n$th term of the sequence $S_A$ we can see that
\begin{equation}
S^n_A = \sqrt[n]{A_n}=\sqrt[n]{\dfrac{P_n}{n+1}} = \sqrt[n]{\dfrac{2^n}{n+1}}.
\end{equation}
We wish to evaluate
\begin{equation}
\lim\limits_{n \rightarrow \infty}{S_A} = \lim\limits_{n \rightarrow \infty}{\sqrt[n]{\dfrac{2^n}{n+1}}}.
\end{equation}
We can reduce by first rewritting the limit as an exponential and applying the logarithm through:
\begin{equation}
\lim\limits_{n \rightarrow \infty}{\sqrt[n]{\dfrac{2^n}{n+1}}} =
\lim\limits_{n \rightarrow \infty}{e^{\ln\left(\left(\dfrac{2^n}{n+1}\right)^{\frac{1}{n}}\right)}} =
\lim\limits_{n \rightarrow \infty}{e^{\dfrac{\ln\left(\frac{2^n}{n+1}\right)}{n}}}.
\end{equation}
Pulling out the exponential function and expanding the logarithms,
\begin{equation}
\lim\limits_{n \rightarrow \infty}{e^{\dfrac{\ln\left(\frac{2^n}{n+1}\right)}{n}}} = e^{\lim\limits_{n \rightarrow \infty}{\dfrac{n \ln(2) - \ln(n+1)}{n}}} =
e^{\lim\limits_{n \rightarrow \infty}{\ln(2)}-\lim\limits_{n \rightarrow \infty}{\dfrac{\ln(n+1)}{n}}}
\end{equation}
Since $n$ grows faster than $\ln(n+1)$, $\lim\limits_{n \rightarrow \infty}{\frac{\ln(n+1)}{n}}=0$, and $\ln(2)$ is a constant,
\begin{equation}
e^{\lim\limits_{n \rightarrow \infty}{\ln(2)}-\lim\limits_{n \rightarrow \infty}{\dfrac{\ln(n+1)}{n}}} = e^{\ln(2)} = 2,
\end{equation}
giving is the result that
\begin{equation}
\lim\limits_{n \rightarrow \infty}{S_A} = 2.
\end{equation}

Let $Q_n$ be the product of the sequence (\ref{binomial}0):

\begin{equation}

Q_n = {n \choose 0}{n \choose 1}{n \choose 2}{\cdots}{n \choose n} = \prod\limits_{i=0}^{n}{n \choose i}.

\end{equation}

Recall that

\begin{equation}

{n \choose k} = \dfrac{n!}{k!(n-k)!}.

\end{equation}

So,

\begin{equation}

Q_n = \prod\limits_{i=0}^{n}{n \choose i} = \dfrac{n!}{0!(n-0)!}\dfrac{n!}{1!(n-1)!}\dfrac{n!}{2!(n-2)!}\cdots\dfrac{n!}{n!(n-n)!}.

\end{equation}

We can pull out the constant factors in the denominator, leaving us with

\begin{equation}

Q_n = \dfrac{1}{0!1!2!\cdots n!}\left(\dfrac{n!}{(n-0)!}\dfrac{n!}{(n-1)!}\dfrac{n!}{(n-2)!}\cdots\dfrac{n!}{(n-n)!}\right).

\end{equation}

Let us consider what happens when we divide out each factor inside the parenthesis. The first factor will be $1$, the second will be $n$, the third will be $n(n-1)$, the fourth will be $n(n-1)(n-2)$ and so forth. How many $n$s will this product have? Each factor (except the first) has an $n$ and $Q_n$ has $n+1$ of these factors, so the product will have $n$ number of $n$s, or $n^n$. Likewise for $(n-1)$ there will be $(n-1)$ factors with it, so the product will have $(n-1)^(n-1)$. Continuing this we arrive at

\begin{equation}\label{qn_powers}

Q_n = \dfrac{n^n(n-1)^{n-1}(n-2)^{n-2}\cdots 2^2 1}{0!1!2!\cdots n!}.

\end{equation}

The numerator of (\ref{qn_powers}) is the *hyperfactorial* [3], denoted as $H(n)$ . The denominator is the *Barnes G-function* [4], denoted as $G(n)$ for $n+2$. So, we can rewrite (\ref{qn_powers}) as

\begin{equation}\label{qcompact}

Q_n = \dfrac{H(n)}{G(n+2)}.

\end{equation}

The Barnes G-function is defined as

\begin{equation}\label{gfunction}

G(n) = \dfrac{\Gamma(n)^{n-1}}{H(n-1)}.

\end{equation}

Plugging (\ref{gfunction}) into (\ref{qcompact}), we now have

\begin{equation}

Q_n = \dfrac{H(n)}{G(n+2)} = \dfrac{H(n)}{\left[\dfrac{\Gamma(n+2)^{n+2-1}}{H(n+2-1)}\right]} = \dfrac{H(n)H(n+1)}{\Gamma(n+2)^{n+1}}.

\end{equation}

Because $\Gamma(n)=(n-1)!$ when $n$ is a non-negative integer (which is always true for our $n$) the denominator can be simplified to

\begin{equation}

\dfrac{H(n)H(n+1)}{(n+1)!^{n+1}} = \dfrac{H(n)H(n)(n+1)^{n+1}}{n!^{n+1}(n+1)^{n+1}} = \dfrac{H(n)^2}{n!^{n+1}}.

\end{equation}

We wish to find the geometric mean of (\ref{binomial}). The geometric mean of $n$ numbers is the product of the numbers to the $n$th root; $Q_n$ is the product of (\ref{binomial}), which has $n+1$ numbers, so the geometric mean $G_n$ of (\ref{binomial}) is

\begin{equation}

G_n=\sqrt[n+1]{Q_n} = \sqrt[n+1]{\dfrac{H(n)^2}{n!^{n+1}}} = \dfrac{H(n)^{\frac{2}{n+1}}}{n!}.

\end{equation}

Ultimately we are interested in the convergence of the sequence $S_G = G_1,\sqrt{G_2},\sqrt[3]{G_3}$, so if $S^n_G$ is the $n$th term of this sequence, then

\begin{equation}

S^n_G = \sqrt[n]{G_n} = \sqrt[n]{\dfrac{H(n)^{\frac{2}{n+1}}}{n!}}.

\end{equation}

Does $S_G$ converge? To answer this we will first consider a new sequence $S’$ where $S’_n$, the $n$th term of $S’$, is

\begin{equation}\label{sprime}

S’_n = (S^n_G)^2.

\end{equation}

In other words, the terms of $S’$ are the squares of the terms of $S_G$. Continuing,

\begin{equation}

S’_n = (S^n_G)^2 = \left(\dfrac{H(n)^{\frac{2}{n+1}}}{n!}\right)^{\frac{2}{n}} = \dfrac{H(n)^{\frac{4}{n^2+n}}}{n!^{\frac{2}{n}}}

\end{equation}

Now is a convenient time to mention [5]:

\begin{equation}\label{e}

\lim\limits_{n \rightarrow \infty}{\dfrac{n}{\sqrt[n]{n!}}} = e.

\end{equation}

If we take the limit of the difference of (\ref{e}) and $S’$, we find

\begin{equation}

\lim\limits_{n \rightarrow \infty}{\left(\dfrac{n}{\sqrt[n]{n!}} – \dfrac{H(n)^{\frac{4}{n^2+n}}}{n!^{\frac{2}{n}}}\right)} =

\lim\limits_{n \rightarrow \infty}{\left(\dfrac{n\sqrt[n]{n!} – H(n)^{\frac{4}{n^2+n}}}{n!^{\frac{2}{n}}}\right)} = 0.

\end{equation}

Since the difference of the two limits is zero, we can conclude

\begin{equation} \label{squarede}

\lim\limits_{n \rightarrow \infty}{\left(\dfrac{n}{\sqrt[n]{n!}} – \dfrac{H(n)^{\frac{4}{n^2+n}}}{n!^{\frac{2}{n}}}\right)} = 0 \Rightarrow

\lim\limits_{n \rightarrow \infty}{\dfrac{n}{\sqrt[n]{n!}}} = \lim\limits_{n \rightarrow \infty}\dfrac{H(n)^{\frac{4}{n^2+n}}}{n!^{\frac{2}{n}}} \Rightarrow \lim\limits_{n \rightarrow \infty}\dfrac{H(n)^{\frac{4}{n^2+n}}}{n!^{\frac{2}{n}}} = e.

\end{equation}

$S_G$ is bounded above by $S’$, so it converges and by (\ref{squarede}) and (\ref{sprime})

\begin{equation}

\lim\limits_{n \rightarrow \infty}{S’} = \lim\limits_{n \rightarrow \infty}{(S_G)^2} \Rightarrow

\lim\limits_{n \rightarrow \infty}{S’} = \left(\lim\limits_{n \rightarrow \infty}{S_G}\right)^2 \Rightarrow e = \left(\lim\limits_{n \rightarrow \infty}{S_G}\right)^2.

\end{equation}

Finally,

\begin{equation}

\lim\limits_{n \rightarrow \infty}{S_G} = \sqrt{e}.

\end{equation}

[1] Katz, Victor J. A History of Mathematics: An Introduction. Boston: Addison-Wesley, 2009.

[2] Young, Robert M. Excursions in Calculus: An Interplay of the Continuous and the Discrete. Washington, D.C.: The Mathematical Association of America, 1992.

[3] Azarian, M K. 2007. On the hyperfactorial function, hypertriangular function, and discriminants of certain polynomials. International Journal of Pure and Applied Mathematics 36, (2): 249-255

[4] Barnes, E. W. 1900. The theory of the G-function. Quarterly Journal of Pure and Applied Mathematics 31, 264-314.

[5] Weisstein, Eric W. `Stirling’s Approximation.” From MathWorld–A Wolfram Web Resource. http://mathworld.wolfram.com/StirlingsApproximation.html`